diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index c84a97d7..c2e9547c 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -28,8 +28,9 @@ jobs: tox -e lint - name: Integration Tests env: - PLATFORMIO_TEST_ACCOUNT_LOGIN: ${{ secrets.PLATFORMIO_TEST_ACCOUNT_LOGIN }} - PLATFORMIO_TEST_ACCOUNT_PASSWORD: ${{ secrets.PLATFORMIO_TEST_ACCOUNT_PASSWORD }} + TEST_EMAIL_LOGIN: ${{ secrets.TEST_EMAIL_LOGIN }} + TEST_EMAIL_PASSWORD: ${{ secrets.TEST_EMAIL_PASSWORD }} + TEST_EMAIL_POP3_SERVER: ${{ secrets.TEST_EMAIL_POP3_SERVER }} run: | tox -e testcore diff --git a/platformio/clients/account.py b/platformio/clients/account.py index 9534777d..31e34f78 100644 --- a/platformio/clients/account.py +++ b/platformio/clients/account.py @@ -189,11 +189,14 @@ class AccountClient(RESTClient): # pylint:disable=too-many-public-methods app.set_state_item("account", account) return result - def create_org(self, orgname, email, display_name): + def destroy_account(self): + return self.send_auth_request("delete", "/v1/account") + + def create_org(self, orgname, email, displayname): return self.send_auth_request( "post", "/v1/orgs", - data={"orgname": orgname, "email": email, "displayname": display_name}, + data={"orgname": orgname, "email": email, "displayname": displayname}, ) def get_org(self, orgname): @@ -207,6 +210,9 @@ class AccountClient(RESTClient): # pylint:disable=too-many-public-methods "put", "/v1/orgs/%s" % orgname, data={k: v for k, v in data.items() if v} ) + def destroy_org(self, orgname): + return self.send_auth_request("delete", "/v1/orgs/%s" % orgname,) + def add_org_owner(self, orgname, username): return self.send_auth_request( "post", "/v1/orgs/%s/owners" % orgname, data={"username": username}, diff --git a/platformio/commands/account.py b/platformio/commands/account.py index c254dbee..3a1492ec 100644 --- a/platformio/commands/account.py +++ b/platformio/commands/account.py @@ -178,6 +178,23 @@ def account_update(current_password, **kwargs): return click.secho("Please re-login.", fg="yellow") +@cli.command("destroy", short_help="Destroy account") +def account_destroy(): + client = AccountClient() + click.confirm( + "Are you sure you want to delete the %s user account?\n" + "Warning! All linked data will be permanently removed and can not be restored." + % client.get_account_info().get("profile").get("username"), + abort=True, + ) + client.destroy_account() + try: + client.logout() + except AccountNotAuthorized: + pass + return click.secho("User account has been destroyed.", fg="green",) + + @cli.command("show", short_help="PIO Account information") @click.option("--offline", is_flag=True) @click.option("--json-output", is_flag=True) diff --git a/platformio/commands/org.py b/platformio/commands/org.py index 7d62120f..a7e0f1e9 100644 --- a/platformio/commands/org.py +++ b/platformio/commands/org.py @@ -39,10 +39,10 @@ def validate_orgname(value): @click.option( "--email", callback=lambda _, __, value: validate_email(value) if value else value ) -@click.option("--display-name",) -def org_create(orgname, email, display_name): +@click.option("--displayname",) +def org_create(orgname, email, displayname): client = AccountClient() - client.create_org(orgname, email, display_name) + client.create_org(orgname, email, displayname) return click.secho( "The organization %s has been successfully created." % orgname, fg="green", ) @@ -82,7 +82,7 @@ def org_list(json_output): "--new-orgname", callback=lambda _, __, value: validate_orgname(value), ) @click.option("--email") -@click.option("--display-name",) +@click.option("--displayname",) def org_update(orgname, **kwargs): client = AccountClient() org = client.get_org(orgname) @@ -107,6 +107,20 @@ def org_update(orgname, **kwargs): ) +@cli.command("destroy", short_help="Destroy organization") +@click.argument("orgname") +def account_destroy(orgname): + client = AccountClient() + click.confirm( + "Are you sure you want to delete the %s organization account?\n" + "Warning! All linked data will be permanently removed and can not be restored." + % orgname, + abort=True, + ) + client.destroy_org(orgname) + return click.secho("Organization %s has been destroyed." % orgname, fg="green",) + + @cli.command("add", short_help="Add a new owner to organization") @click.argument("orgname",) @click.argument("username",) diff --git a/tests/commands/test_account.py b/tests/commands/test_account.py index 1be778eb..221b724a 100644 --- a/tests/commands/test_account.py +++ b/tests/commands/test_account.py @@ -17,34 +17,29 @@ import os import time import pytest +import requests from platformio.commands.account import cli as cmd_account - -pytestmark = pytest.mark.skipif( - not ( - os.environ.get("PLATFORMIO_TEST_ACCOUNT_LOGIN") - and os.environ.get("PLATFORMIO_TEST_ACCOUNT_PASSWORD") - ), - reason="requires PLATFORMIO_TEST_ACCOUNT_LOGIN, PLATFORMIO_TEST_ACCOUNT_PASSWORD environ variables", -) +from platformio.commands.package import cli as cmd_package +from platformio.downloader import FileDownloader +from platformio.unpacker import FileUnpacker -@pytest.fixture(scope="session") -def credentials(): - return { - "login": os.environ["PLATFORMIO_TEST_ACCOUNT_LOGIN"], - "password": os.environ["PLATFORMIO_TEST_ACCOUNT_PASSWORD"], - } - - -def test_account_register_with_already_exists_username( - clirunner, credentials, isolated_pio_home +@pytest.mark.skipif( + not os.environ.get("TEST_EMAIL_LOGIN"), + reason="requires TEST_EMAIL_LOGIN, TEST_EMAIL_PASSWORD environ variables", +) # pylint:disable=too-many-arguments +def test_account( + clirunner, validate_cliresult, receive_email, isolated_pio_home, tmpdir_factory ): - username = credentials["login"] - email = "test@test.com" - if "@" in credentials["login"]: - username = "Testusername" - email = credentials["login"] + username = "test-piocore-%s" % str(int(time.time() * 1000)) + splited_email = os.environ.get("TEST_EMAIL_LOGIN").split("@") + email = "%s+%s@%s" % (splited_email[0], username, splited_email[1]) + firstname = "Test" + lastname = "User" + password = "Qwerty123!" + + # pio account register result = clirunner.invoke( cmd_account, [ @@ -54,345 +49,33 @@ def test_account_register_with_already_exists_username( "-e", email, "-p", - credentials["password"], + password, "--firstname", - "First", + firstname, "--lastname", - "Last", + lastname, ], ) - assert result.exit_code > 0 - assert result.exception - assert "User with same username already exists" in str( - result.exception - ) or "User with same email already exists" in str(result.exception) + validate_cliresult(result) + # email verification + result = receive_email(email) + link = ( + result.split("Click on the link below to start this process.")[1] + .split("This link will expire within 12 hours.")[0] + .strip() + ) + session = requests.Session() + result = session.get(link).text + link = result.split(' 0 - assert result.exception - assert "Invalid user credentials" in str(result.exception) - - -def test_account_login(clirunner, credentials, validate_cliresult, isolated_pio_home): + # pio account login + result = clirunner.invoke(cmd_account, ["login", "-u", username, "-p", password],) + validate_cliresult(result) try: - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - assert "Successfully logged in!" in result.output - - with open(str(isolated_pio_home.join("appstate.json"))) as fp: - appstate = json.load(fp) - assert appstate.get("account") - assert appstate.get("account").get("email") - assert appstate.get("account").get("username") - assert appstate.get("account").get("auth") - assert appstate.get("account").get("auth").get("access_token") - assert appstate.get("account").get("auth").get("access_token_expire") - assert appstate.get("account").get("auth").get("refresh_token") - - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - assert result.exit_code > 0 - assert result.exception - assert "You are already authorized with" in str(result.exception) - finally: - clirunner.invoke(cmd_account, ["logout"]) - - -def test_account_logout(clirunner, credentials, validate_cliresult, isolated_pio_home): - try: - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - - result = clirunner.invoke(cmd_account, ["logout"]) - validate_cliresult(result) - assert "Successfully logged out" in result.output - - result = clirunner.invoke(cmd_account, ["logout"]) - assert result.exit_code > 0 - assert result.exception - assert "You are not authorized! Please log in to PIO Account" in str( - result.exception - ) - finally: - clirunner.invoke(cmd_account, ["logout"]) - - -@pytest.mark.skip_ci -def test_account_password_change_with_invalid_old_password( - clirunner, credentials, validate_cliresult -): - try: - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - - result = clirunner.invoke( - cmd_account, - ["password", "--old-password", "test", "--new-password", "test"], - ) - assert result.exit_code > 0 - assert result.exception - assert ( - "Invalid request data for new_password -> " - "'Password must contain at least 8 " - "characters including a number and a lowercase letter'" - in str(result.exception) - ) - finally: - clirunner.invoke(cmd_account, ["logout"]) - - -def test_account_password_change_with_invalid_new_password_format( - clirunner, credentials, validate_cliresult -): - try: - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - - result = clirunner.invoke( - cmd_account, - [ - "password", - "--old-password", - credentials["password"], - "--new-password", - "test", - ], - ) - assert result.exit_code > 0 - assert result.exception - assert ( - "Invalid request data for new_password -> " - "'Password must contain at least 8 characters" - " including a number and a lowercase letter'" in str(result.exception) - ) - - finally: - clirunner.invoke(cmd_account, ["logout"]) - - -@pytest.mark.skip_ci -def test_account_password_change( - clirunner, credentials, validate_cliresult, isolated_pio_home -): - try: - result = clirunner.invoke( - cmd_account, - [ - "password", - "--old-password", - credentials["password"], - "--new-password", - "Testpassword123", - ], - ) - assert result.exit_code > 0 - assert result.exception - assert "You are not authorized! Please log in to PIO Account" in str( - result.exception - ) - - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - - result = clirunner.invoke( - cmd_account, - [ - "password", - "--old-password", - credentials["password"], - "--new-password", - "Testpassword123", - ], - ) - validate_cliresult(result) - assert "Password successfully changed!" in result.output - - result = clirunner.invoke(cmd_account, ["logout"]) - validate_cliresult(result) - - result = clirunner.invoke( - cmd_account, ["login", "-u", credentials["login"], "-p", "Testpassword123"], - ) - validate_cliresult(result) - - result = clirunner.invoke( - cmd_account, - [ - "password", - "--old-password", - "Testpassword123", - "--new-password", - credentials["password"], - ], - ) - validate_cliresult(result) - assert "Password successfully changed!" in result.output - - finally: - clirunner.invoke(cmd_account, ["logout"]) - - -@pytest.mark.skip_ci -def test_account_token_with_invalid_password( - clirunner, credentials, validate_cliresult -): - try: - result = clirunner.invoke( - cmd_account, ["token", "--password", credentials["password"],], - ) - assert result.exit_code > 0 - assert result.exception - assert "You are not authorized! Please log in to PIO Account" in str( - result.exception - ) - - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - - result = clirunner.invoke(cmd_account, ["token", "--password", "test",],) - assert result.exit_code > 0 - assert result.exception - assert "Invalid user password" in str(result.exception) - - finally: - clirunner.invoke(cmd_account, ["logout"]) - - -def test_account_token(clirunner, credentials, validate_cliresult, isolated_pio_home): - try: - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - - result = clirunner.invoke( - cmd_account, ["token", "--password", credentials["password"],], - ) - validate_cliresult(result) - assert "Personal Authentication Token:" in result.output - token = result.output.strip().split(": ")[-1] - - result = clirunner.invoke( - cmd_account, - ["token", "--password", credentials["password"], "--json-output"], - ) - validate_cliresult(result) - json_result = json.loads(result.output.strip()) - assert json_result - assert json_result.get("status") == "success" - assert json_result.get("result") == token - token = json_result.get("result") - - clirunner.invoke(cmd_account, ["logout"]) - - result = clirunner.invoke( - cmd_account, ["token", "--password", credentials["password"],], - ) - assert result.exit_code > 0 - assert result.exception - assert "You are not authorized! Please log in to PIO Account" in str( - result.exception - ) - - os.environ["PLATFORMIO_AUTH_TOKEN"] = token - - result = clirunner.invoke( - cmd_account, - ["token", "--password", credentials["password"], "--json-output"], - ) - validate_cliresult(result) - json_result = json.loads(result.output.strip()) - assert json_result - assert json_result.get("status") == "success" - assert json_result.get("result") == token - - os.environ.pop("PLATFORMIO_AUTH_TOKEN") - - finally: - clirunner.invoke(cmd_account, ["logout"]) - - -@pytest.mark.skip_ci -def test_account_token_with_refreshing( - clirunner, credentials, validate_cliresult, isolated_pio_home -): - try: - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - - result = clirunner.invoke( - cmd_account, - ["token", "--password", credentials["password"], "--json-output"], - ) - validate_cliresult(result) - json_result = json.loads(result.output.strip()) - assert json_result - assert json_result.get("status") == "success" - assert json_result.get("result") - token = json_result.get("result") - - result = clirunner.invoke( - cmd_account, - [ - "token", - "--password", - credentials["password"], - "--json-output", - "--regenerate", - ], - ) - validate_cliresult(result) - json_result = json.loads(result.output.strip()) - assert json_result - assert json_result.get("status") == "success" - assert json_result.get("result") - assert token != json_result.get("result") - finally: - clirunner.invoke(cmd_account, ["logout"]) - - -def test_account_summary(clirunner, credentials, validate_cliresult, isolated_pio_home): - try: - result = clirunner.invoke(cmd_account, ["show"],) - assert result.exit_code > 0 - assert result.exception - assert "You are not authorized! Please log in to PIO Account" in str( - result.exception - ) - - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - + # pio account summary result = clirunner.invoke(cmd_account, ["show", "--json-output", "--offline"]) validate_cliresult(result) json_result = json.loads(result.output.strip()) @@ -405,9 +88,8 @@ def test_account_summary(clirunner, credentials, validate_cliresult, isolated_pi result = clirunner.invoke(cmd_account, ["show"]) validate_cliresult(result) - assert credentials["login"] in result.output - assert "Community" in result.output - assert "100 Concurrent Remote Agents" in result.output + assert username in result.output + # assert "100 Concurrent Remote Agents" in result.output result = clirunner.invoke(cmd_account, ["show", "--json-output"]) validate_cliresult(result) @@ -416,9 +98,9 @@ def test_account_summary(clirunner, credentials, validate_cliresult, isolated_pi assert json_result.get("profile") assert json_result.get("profile").get("username") assert json_result.get("profile").get("email") - assert credentials["login"] == json_result.get("profile").get( + assert username == json_result.get("profile").get( "username" - ) or credentials["login"] == json_result.get("profile").get("email") + ) or username == json_result.get("profile").get("email") assert json_result.get("profile").get("firstname") assert json_result.get("profile").get("lastname") assert json_result.get("packages") @@ -433,147 +115,121 @@ def test_account_summary(clirunner, credentials, validate_cliresult, isolated_pi assert json_result.get("profile") assert json_result.get("profile").get("username") assert json_result.get("profile").get("email") - assert credentials["login"] == json_result.get("profile").get( + assert username == json_result.get("profile").get( "username" - ) or credentials["login"] == json_result.get("profile").get("email") + ) or username == json_result.get("profile").get("email") assert json_result.get("profile").get("firstname") assert json_result.get("profile").get("lastname") assert json_result.get("packages") assert json_result.get("packages")[0].get("name") assert json_result.get("packages")[0].get("path") assert json_result.get("subscriptions") is not None - finally: + + # pio account token + result = clirunner.invoke(cmd_account, ["token", "--password", password,],) + validate_cliresult(result) + assert "Personal Authentication Token:" in result.output + token = result.output.strip().split(": ")[-1] + + result = clirunner.invoke( + cmd_account, ["token", "--password", password, "--json-output"], + ) + validate_cliresult(result) + json_result = json.loads(result.output.strip()) + assert json_result + assert json_result.get("status") == "success" + assert json_result.get("result") == token + token = json_result.get("result") + clirunner.invoke(cmd_account, ["logout"]) - -@pytest.mark.skip_ci -def test_account_profile_update_with_invalid_password( - clirunner, credentials, validate_cliresult -): - try: - result = clirunner.invoke( - cmd_account, ["update", "--current-password", credentials["password"]], - ) + result = clirunner.invoke(cmd_account, ["token", "--password", password,],) assert result.exit_code > 0 assert result.exception assert "You are not authorized! Please log in to PIO Account" in str( result.exception ) + os.environ["PLATFORMIO_AUTH_TOKEN"] = token + result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], + cmd_account, ["token", "--password", password, "--json-output"], + ) + validate_cliresult(result) + json_result = json.loads(result.output.strip()) + assert json_result + assert json_result.get("status") == "success" + assert json_result.get("result") == token + + os.environ.pop("PLATFORMIO_AUTH_TOKEN") + + result = clirunner.invoke( + cmd_account, ["login", "-u", username, "-p", password], ) validate_cliresult(result) - firstname = "First " + str(int(time.time() * 1000)) - + # pio account password + new_password = "Testpassword123" result = clirunner.invoke( cmd_account, - ["update", "--current-password", "test", "--firstname", firstname], + ["password", "--old-password", password, "--new-password", new_password,], ) - assert result.exit_code > 0 - assert result.exception - assert "Invalid user password" in str(result.exception) - finally: + validate_cliresult(result) + assert "Password successfully changed!" in result.output + clirunner.invoke(cmd_account, ["logout"]) - -@pytest.mark.skip_ci -def test_account_profile_update_only_firstname_and_lastname( - clirunner, credentials, validate_cliresult, isolated_pio_home -): - try: result = clirunner.invoke( - cmd_account, ["update", "--current-password", credentials["password"]], - ) - assert result.exit_code > 0 - assert result.exception - assert "You are not authorized! Please log in to PIO Account" in str( - result.exception - ) - - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], + cmd_account, ["login", "-u", username, "-p", new_password], ) validate_cliresult(result) + result = clirunner.invoke( + cmd_account, + ["password", "--old-password", new_password, "--new-password", password,], + ) + validate_cliresult(result) + + # pio account update firstname = "First " + str(int(time.time() * 1000)) lastname = "Last" + str(int(time.time() * 1000)) - result = clirunner.invoke( - cmd_account, - [ - "update", - "--current-password", - credentials["password"], - "--firstname", - firstname, - "--lastname", - lastname, - ], - ) - validate_cliresult(result) - assert "Profile successfully updated!" in result.output - - result = clirunner.invoke(cmd_account, ["show", "--json-output"]) - validate_cliresult(result) - json_result = json.loads(result.output.strip()) - assert json_result.get("profile").get("firstname") == firstname - assert json_result.get("profile").get("lastname") == lastname - - finally: - clirunner.invoke(cmd_account, ["logout"]) - - -@pytest.mark.skip_ci -def test_account_profile_update( - clirunner, credentials, validate_cliresult, isolated_pio_home -): - try: - result = clirunner.invoke( - cmd_account, ["update", "--current-password", credentials["password"]], - ) - assert result.exit_code > 0 - assert result.exception - assert "You are not authorized! Please log in to PIO Account" in str( - result.exception - ) - - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) - validate_cliresult(result) - - result = clirunner.invoke(cmd_account, ["show", "--json-output"]) - validate_cliresult(result) - json_result = json.loads(result.output.strip()) - - firstname = "First " + str(int(time.time() * 1000)) - lastname = "Last" + str(int(time.time() * 1000)) - - old_username = json_result.get("profile").get("username") new_username = "username" + str(int(time.time() * 1000))[-5:] - + new_email = "%s+new-%s@%s" % (splited_email[0], username, splited_email[1]) result = clirunner.invoke( cmd_account, [ "update", "--current-password", - credentials["password"], + password, "--firstname", firstname, "--lastname", lastname, "--username", new_username, + "--email", + new_email, ], ) validate_cliresult(result) assert "Profile successfully updated!" in result.output - assert "Please re-login." in result.output + assert ( + "Please check your mail to verify your new email address and re-login. " + in result.output + ) + + result = receive_email(new_email) + link = ( + result.split("Click on the link below to start this process.")[1] + .split("This link will expire within 12 hours.")[0] + .strip() + ) + session = requests.Session() + result = session.get(link).text + link = result.split(' 0 @@ -583,27 +239,39 @@ def test_account_profile_update( ) result = clirunner.invoke( - cmd_account, ["login", "-u", new_username, "-p", credentials["password"]], + cmd_account, ["login", "-u", new_username, "-p", password], ) validate_cliresult(result) - result = clirunner.invoke( - cmd_account, - [ - "update", - "--current-password", - credentials["password"], - "--username", - old_username, - ], - ) - validate_cliresult(result) - assert "Profile successfully updated!" in result.output - assert "Please re-login." in result.output + # pio account destroy with linked resource - result = clirunner.invoke( - cmd_account, ["login", "-u", old_username, "-p", credentials["password"]], + package_url = "https://github.com/bblanchon/ArduinoJson/archive/v6.11.0.tar.gz" + + tmp_dir = tmpdir_factory.mktemp("package") + fd = FileDownloader(package_url, str(tmp_dir)) + pkg_dir = tmp_dir.mkdir("raw_package") + fd.start(with_progress=False, silent=True) + with FileUnpacker(fd.get_filepath()) as unpacker: + unpacker.unpack(str(pkg_dir), with_progress=False, silent=True) + + result = clirunner.invoke(cmd_package, ["publish", str(pkg_dir)],) + validate_cliresult(result) + try: + result = receive_email(new_email) + assert "Congrats" in result + assert "was published" in result + except: # pylint:disable=bare-except + pass + + result = clirunner.invoke(cmd_account, ["destroy"], "y") + assert result.exit_code != 0 + assert ( + "We can not destroy the %s account due to 1 linked resources from registry" + % username ) + + result = clirunner.invoke(cmd_package, ["unpublish", "ArduinoJson"],) validate_cliresult(result) finally: - clirunner.invoke(cmd_account, ["logout"]) + result = clirunner.invoke(cmd_account, ["destroy"], "y") + validate_cliresult(result) diff --git a/tests/commands/test_orgs.py b/tests/commands/test_orgs.py index 4650caaf..3af38e83 100644 --- a/tests/commands/test_orgs.py +++ b/tests/commands/test_orgs.py @@ -14,142 +14,150 @@ import json import os +import time import pytest +import requests from platformio.commands.account import cli as cmd_account from platformio.commands.org import cli as cmd_org -pytestmark = pytest.mark.skipif( - not ( - os.environ.get("PLATFORMIO_TEST_ACCOUNT_LOGIN") - and os.environ.get("PLATFORMIO_TEST_ACCOUNT_PASSWORD") - ), - reason="requires PLATFORMIO_TEST_ACCOUNT_LOGIN, PLATFORMIO_TEST_ACCOUNT_PASSWORD environ variables", -) +@pytest.mark.skipif( + not os.environ.get("TEST_EMAIL_LOGIN"), + reason="requires TEST_EMAIL_LOGIN, TEST_EMAIL_PASSWORD environ variables", +) # pylint:disable=too-many-arguments +def test_org(clirunner, validate_cliresult, receive_email, isolated_pio_home): + username = "test-piocore-%s" % str(int(time.time() * 1000)) + splited_email = os.environ.get("TEST_EMAIL_LOGIN").split("@") + email = "%s+%s@%s" % (splited_email[0], username, splited_email[1]) + firstname = "Test" + lastname = "User" + password = "Qwerty123!" -@pytest.fixture(scope="session") -def credentials(): - return { - "login": os.environ["PLATFORMIO_TEST_ACCOUNT_LOGIN"], - "password": os.environ["PLATFORMIO_TEST_ACCOUNT_PASSWORD"], - } + # pio account register + result = clirunner.invoke( + cmd_account, + [ + "register", + "-u", + username, + "-e", + email, + "-p", + password, + "--firstname", + firstname, + "--lastname", + lastname, + ], + ) + validate_cliresult(result) + # email verification + result = receive_email(email) + link = ( + result.split("Click on the link below to start this process.")[1] + .split("This link will expire within 12 hours.")[0] + .strip() + ) + session = requests.Session() + result = session.get(link).text + link = result.split('= 3 - check = False - for org in json_result: - assert "orgname" in org - orgname = org["orgname"] - assert "displayname" in org - assert "email" in org - assert "owners" in org - for owner in org.get("owners"): - assert "username" in owner - check = owner["username"] == credentials["login"] if not check else True - assert "firstname" in owner - assert "lastname" in owner - assert check + assert json_result == [ + { + "orgname": new_orgname, + "displayname": new_display_name, + "email": email, + "owners": [ + {"username": username, "firstname": firstname, "lastname": lastname} + ], + } + ] - result = clirunner.invoke(cmd_org, ["add", orgname, "ivankravets"],) + result = clirunner.invoke( + cmd_org, + [ + "update", + new_orgname, + "--new-orgname", + orgname, + "--displayname", + display_name, + ], + ) validate_cliresult(result) - - result = clirunner.invoke(cmd_org, ["list", "--json-output"],) - validate_cliresult(result) - json_result = json.loads(result.output.strip()) - assert len(json_result) >= 3 - check = False - for item in json_result: - if item["orgname"] != orgname: - continue - for owner in item.get("owners"): - check = owner["username"] == "ivankravets" if not check else True - assert check - - result = clirunner.invoke(cmd_org, ["remove", orgname, "ivankravets"],) - validate_cliresult(result) - - result = clirunner.invoke(cmd_org, ["list", "--json-output"],) - validate_cliresult(result) - json_result = json.loads(result.output.strip()) - assert len(json_result) >= 3 - check = False - for item in json_result: - if item["orgname"] != orgname: - continue - for owner in item.get("owners"): - check = owner["username"] == "ivankravets" if not check else True - assert not check finally: - clirunner.invoke(cmd_account, ["logout"]) - - -@pytest.mark.skip -def test_org_update(clirunner, credentials, validate_cliresult, isolated_pio_home): - try: - result = clirunner.invoke( - cmd_account, - ["login", "-u", credentials["login"], "-p", credentials["password"]], - ) + result = clirunner.invoke(cmd_org, ["destroy", orgname], "y") validate_cliresult(result) - assert "Successfully logged in!" in result.output - - result = clirunner.invoke(cmd_org, ["list", "--json-output"],) + result = clirunner.invoke(cmd_account, ["destroy"], "y") validate_cliresult(result) - json_result = json.loads(result.output.strip()) - assert len(json_result) >= 3 - org = json_result[0] - assert "orgname" in org - assert "displayname" in org - assert "email" in org - assert "owners" in org - - old_orgname = org["orgname"] - if len(old_orgname) > 10: - new_orgname = "neworg" + org["orgname"][6:] - - result = clirunner.invoke( - cmd_org, ["update", old_orgname, "--new-orgname", new_orgname], - ) - validate_cliresult(result) - - result = clirunner.invoke( - cmd_org, ["update", new_orgname, "--new-orgname", old_orgname], - ) - validate_cliresult(result) - - result = clirunner.invoke(cmd_org, ["list", "--json-output"],) - validate_cliresult(result) - assert json.loads(result.output.strip()) == json_result - finally: - clirunner.invoke(cmd_account, ["logout"]) diff --git a/tests/commands/test_teams.py b/tests/commands/test_teams.py index 13e30ce0..92d5226d 100644 --- a/tests/commands/test_teams.py +++ b/tests/commands/test_teams.py @@ -17,123 +17,128 @@ import os import time import pytest +import requests from platformio.commands.account import cli as cmd_account from platformio.commands.org import cli as cmd_org from platformio.commands.team import cli as cmd_team -pytestmark = pytest.mark.skipif( - not ( - os.environ.get("PLATFORMIO_TEST_ACCOUNT_LOGIN") - and os.environ.get("PLATFORMIO_TEST_ACCOUNT_PASSWORD") - ), - reason="requires PLATFORMIO_TEST_ACCOUNT_LOGIN, PLATFORMIO_TEST_ACCOUNT_PASSWORD environ variables", -) +@pytest.mark.skipif( + not os.environ.get("TEST_EMAIL_LOGIN"), + reason="requires TEST_EMAIL_LOGIN, TEST_EMAIL_PASSWORD environ variables", +) # pylint:disable=too-many-arguments +def test_teams(clirunner, validate_cliresult, receive_email, isolated_pio_home): + username = "test-piocore-%s" % str(int(time.time() * 1000)) + splited_email = os.environ.get("TEST_EMAIL_LOGIN").split("@") + email = "%s+%s@%s" % (splited_email[0], username, splited_email[1]) + firstname = "Test" + lastname = "User" + password = "Qwerty123!" -@pytest.fixture(scope="session") -def credentials(): - return { - "login": os.environ["PLATFORMIO_TEST_ACCOUNT_LOGIN"], - "password": os.environ["PLATFORMIO_TEST_ACCOUNT_PASSWORD"], - } + # pio account register + result = clirunner.invoke( + cmd_account, + [ + "register", + "-u", + username, + "-e", + email, + "-p", + password, + "--firstname", + firstname, + "--lastname", + lastname, + ], + ) + validate_cliresult(result) + # email verification + result = receive_email(email) + link = ( + result.split("Click on the link below to start this process.")[1] + .split("This link will expire within 12 hours.")[0] + .strip() + ) + session = requests.Session() + result = session.get(link).text + link = result.split('= 3 - orgname = json_result[0].get("orgname") - + # pio team create result = clirunner.invoke( cmd_team, [ "create", "%s:%s" % (orgname, teamname), "--description", - "team for CI test", + team_description, ], ) validate_cliresult(result) + # pio team list result = clirunner.invoke(cmd_team, ["list", "%s" % orgname, "--json-output"],) validate_cliresult(result) json_result = json.loads(result.output.strip()) - assert len(json_result) >= 1 - check = False - for team in json_result: - assert team["id"] - assert team["name"] - if team["name"] == teamname: - check = True - assert "description" in team - assert "members" in team - assert check + for item in json_result: + del item["id"] + assert json_result == [ + {"name": teamname, "description": team_description, "members": []} + ] + # pio team add (member) result = clirunner.invoke( - cmd_team, ["add", "%s:%s" % (orgname, teamname), credentials["login"]], + cmd_team, ["add", "%s:%s" % (orgname, teamname), second_username], ) validate_cliresult(result) result = clirunner.invoke(cmd_team, ["list", "%s" % orgname, "--json-output"],) validate_cliresult(result) - json_result = json.loads(result.output.strip()) - check = False - for team in json_result: - assert team["id"] - assert team["name"] - assert "description" in team - assert "members" in team - if ( - len(team["members"]) > 0 - and team["members"][0]["username"] == credentials["login"] - ): - check = True - assert check + assert second_username in result.output + # pio team remove (member) result = clirunner.invoke( - cmd_team, ["remove", "%s:%s" % (orgname, teamname), credentials["login"]], + cmd_team, ["remove", "%s:%s" % (orgname, teamname), second_username], ) validate_cliresult(result) result = clirunner.invoke(cmd_team, ["list", "%s" % orgname, "--json-output"],) validate_cliresult(result) + assert second_username not in result.output + # pio team update + new_teamname = "new-" + str(int(time.time() * 1000)) + newteam_description = "Updated Description" result = clirunner.invoke( cmd_team, [ "update", "%s:%s" % (orgname, teamname), + "--name", + new_teamname, "--description", - "Updated Description", + newteam_description, ], ) validate_cliresult(result) @@ -141,18 +146,30 @@ def test_teams(clirunner, credentials, validate_cliresult, isolated_pio_home): result = clirunner.invoke(cmd_team, ["list", "%s" % orgname, "--json-output"],) validate_cliresult(result) json_result = json.loads(result.output.strip()) - assert len(json_result) >= 1 - check = False - for team in json_result: - assert team["id"] - assert team["name"] - assert "description" in team - if team.get("description") == "Updated Description": - check = True - assert "members" in team - assert check - finally: - clirunner.invoke( - cmd_team, ["destroy", "%s:%s" % (orgname, teamname),], + for item in json_result: + del item["id"] + assert json_result == [ + {"name": new_teamname, "description": newteam_description, "members": []} + ] + + result = clirunner.invoke( + cmd_team, + [ + "update", + "%s:%s" % (orgname, new_teamname), + "--name", + teamname, + "--description", + team_description, + ], ) - clirunner.invoke(cmd_account, ["logout"]) + validate_cliresult(result) + finally: + result = clirunner.invoke( + cmd_team, ["destroy", "%s:%s" % (orgname, teamname)], "y" + ) + validate_cliresult(result) + result = clirunner.invoke(cmd_org, ["destroy", orgname], "y") + validate_cliresult(result) + result = clirunner.invoke(cmd_account, ["destroy"], "y") + validate_cliresult(result) diff --git a/tests/conftest.py b/tests/conftest.py index f0529146..4b2259ac 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +import email import os +import poplib +import time import pytest from click.testing import CliRunner @@ -53,3 +56,43 @@ def isolated_pio_home(request, tmpdir_factory): @pytest.fixture(scope="function") def without_internet(monkeypatch): monkeypatch.setattr(util, "_internet_on", lambda: False) + + +@pytest.fixture +def receive_email(): # pylint:disable=redefined-outer-name, too-many-locals + def _receive_email(from_who): + test_email = os.environ.get("TEST_EMAIL_LOGIN") + test_password = os.environ.get("TEST_EMAIL_PASSWORD") + pop_server = os.environ.get("TEST_EMAIL_POP3_SERVER") or "pop.gmail.com" + if "gmail" in pop_server: + test_email = "recent:" + test_email + + def get_body(msg): + if msg.is_multipart(): + return get_body(msg.get_payload(0)) + return msg.get_payload(None, True) + + result = None + start_time = time.time() + while not result: + time.sleep(5) + server = poplib.POP3_SSL(pop_server) + server.user(test_email) + server.pass_(test_password) + _, mails, _ = server.list() + for index, _ in enumerate(mails): + _, lines, _ = server.retr(index + 1) + msg_content = b"\n".join(lines) + msg = email.message_from_string( + msg_content.decode("ASCII", errors="surrogateescape") + ) + if from_who not in msg.get("To"): + continue + server.dele(index + 1) + result = get_body(msg).decode() + if time.time() - start_time > 60: + break + server.quit() + return result + + return _receive_email