From 3eebc95e325efa4162750661e25cb750bffb17f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Fri, 19 Nov 2021 22:15:08 +0000 Subject: [PATCH 01/12] feat: add pre-commit hook for conventional commit --- .pre-commit-config.yaml | 8 ++- poetry.lock | 123 +++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 130 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index db23558e..d6f1c122 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: ] - repo: https://github.com/ambv/black - rev: 21.11b0 + rev: 21.11b1 hooks: - id: black @@ -44,3 +44,9 @@ repos: hooks: - id: pyupgrade args: ["--py37-plus", "--keep-runtime-typing"] + + - repo: https://github.com/commitizen-tools/commitizen + rev: v2.20.0 + hooks: + - id: commitizen + stages: [commit-msg] diff --git a/poetry.lock b/poetry.lock index 0ac1ac31..bc81aef6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -24,6 +24,20 @@ doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=6.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"] trio = ["trio (>=0.16)"] +[[package]] +name = "argcomplete" +version = "1.12.3" +description = "Bash tab completion for argparse" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +importlib-metadata = {version = ">=0.23,<5", markers = "python_version == \"3.7\""} + +[package.extras] +test = ["coverage", "flake8", "pexpect", "wheel"] + [[package]] name = "atomicwrites" version = "1.4.0" @@ -147,6 +161,25 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "commitizen" +version = "2.20.0" +description = "Python commitizen client tool" +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0.0" + +[package.dependencies] +argcomplete = ">=1.12.1,<2.0.0" +colorama = ">=0.4.1,<0.5.0" +decli = ">=0.5.2,<0.6.0" +jinja2 = ">=2.10.3" +packaging = ">=19,<22" +pyyaml = ">=3.08" +questionary = ">=1.4.0,<2.0.0" +termcolor = ">=1.1,<2.0" +tomlkit = ">=0.5.3,<1.0.0" + [[package]] name = "coverage" version = "6.1.2" @@ -161,6 +194,14 @@ tomli = {version = "*", optional = true, markers = "extra == \"toml\""} [package.extras] toml = ["tomli"] +[[package]] +name = "decli" +version = "0.5.2" +description = "Minimal, easy-to-use, declarative cli tool" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "distlib" version = "0.3.3" @@ -461,6 +502,17 @@ pyyaml = ">=5.1" toml = "*" virtualenv = ">=20.0.8" +[[package]] +name = "prompt-toolkit" +version = "3.0.22" +description = "Library for building powerful interactive command lines in Python" +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +wcwidth = "*" + [[package]] name = "py" version = "1.11.0" @@ -608,6 +660,20 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "questionary" +version = "1.10.0" +description = "Python library to build pretty command line user prompts ⭐️" +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +prompt_toolkit = ">=2.0,<4.0" + +[package.extras] +docs = ["Sphinx (>=3.3,<4.0)", "sphinx-rtd-theme (>=0.5.0,<0.6.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphinx-copybutton (>=0.3.1,<0.4.0)", "sphinx-autodoc-typehints (>=1.11.1,<2.0.0)"] + [[package]] name = "regex" version = "2021.11.10" @@ -774,6 +840,14 @@ python-versions = ">=3.5" lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +[[package]] +name = "termcolor" +version = "1.1.0" +description = "ANSII Color formatting for output in terminal." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "text-unidecode" version = "1.3" @@ -798,6 +872,14 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "tomlkit" +version = "0.7.2" +description = "Style preserving TOML library" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "typed-ast" version = "1.5.0" @@ -884,6 +966,14 @@ six = ">=1.9.0,<2" docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "zipp" version = "3.6.0" @@ -899,7 +989,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "8274ac946c5476363ccbbd04a5bc8191e448511ad826ede5ce9c31fad2d921ff" +content-hash = "57d65ff3b3e96820652a652da7a553ba3c77f43a5c66578d458aa766882e5d9c" [metadata.files] alabaster = [ @@ -910,6 +1000,10 @@ anyio = [ {file = "anyio-3.3.4-py3-none-any.whl", hash = "sha256:4fd09a25ab7fa01d34512b7249e366cd10358cdafc95022c7ff8c8f8a5026d66"}, {file = "anyio-3.3.4.tar.gz", hash = "sha256:67da67b5b21f96b9d3d65daa6ea99f5d5282cb09f50eb4456f8fb51dffefc3ff"}, ] +argcomplete = [ + {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, + {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, +] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, @@ -950,6 +1044,10 @@ colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] +commitizen = [ + {file = "commitizen-2.20.0-py3-none-any.whl", hash = "sha256:a8c9f75718f0507d703c3b3aeef43bebc3ed0979c8995f9214185956a1bc1c05"}, + {file = "commitizen-2.20.0.tar.gz", hash = "sha256:b52eb35ffbe8281fc3187e648fae2bdd75ed1d17d31c8a0592909ccb7278292f"}, +] coverage = [ {file = "coverage-6.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:675adb3b3380967806b3cbb9c5b00ceb29b1c472692100a338730c1d3e59c8b9"}, {file = "coverage-6.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95a58336aa111af54baa451c33266a8774780242cab3704b7698d5e514840758"}, @@ -999,6 +1097,10 @@ coverage = [ {file = "coverage-6.1.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:eab14fdd410500dae50fd14ccc332e65543e7b39f6fc076fe90603a0e5d2f929"}, {file = "coverage-6.1.2.tar.gz", hash = "sha256:d9a635114b88c0ab462e0355472d00a180a5fbfd8511e7f18e4ac32652e7d972"}, ] +decli = [ + {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, + {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, +] distlib = [ {file = "distlib-0.3.3-py2.py3-none-any.whl", hash = "sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31"}, {file = "distlib-0.3.3.zip", hash = "sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05"}, @@ -1170,6 +1272,10 @@ pre-commit = [ {file = "pre_commit-2.15.0-py2.py3-none-any.whl", hash = "sha256:a4ed01000afcb484d9eb8d504272e642c4c4099bbad3a6b27e519bd6a3e928a6"}, {file = "pre_commit-2.15.0.tar.gz", hash = "sha256:3c25add78dbdfb6a28a651780d5c311ac40dd17f160eb3954a0c59da40a505a7"}, ] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.22-py3-none-any.whl", hash = "sha256:48d85cdca8b6c4f16480c7ce03fd193666b62b0a21667ca56b4bb5ad679d1170"}, + {file = "prompt_toolkit-3.0.22.tar.gz", hash = "sha256:449f333dd120bd01f5d296a8ce1452114ba3a71fae7288d2f0ae2c918764fa72"}, +] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, @@ -1273,6 +1379,10 @@ pyyaml = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] +questionary = [ + {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, + {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, +] regex = [ {file = "regex-2021.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf"}, {file = "regex-2021.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f"}, @@ -1372,6 +1482,9 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] +termcolor = [ + {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, +] text-unidecode = [ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, @@ -1384,6 +1497,10 @@ tomli = [ {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, ] +tomlkit = [ + {file = "tomlkit-0.7.2-py2.py3-none-any.whl", hash = "sha256:173ad840fa5d2aac140528ca1933c29791b79a374a0861a80347f42ec9328117"}, + {file = "tomlkit-0.7.2.tar.gz", hash = "sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754"}, +] typed-ast = [ {file = "typed_ast-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b310a207ee9fde3f46ba327989e6cba4195bc0c8c70a158456e7b10233e6bed"}, {file = "typed_ast-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52ca2b2b524d770bed7a393371a38e91943f9160a190141e0df911586066ecda"}, @@ -1429,6 +1546,10 @@ virtualenv = [ {file = "virtualenv-20.10.0-py2.py3-none-any.whl", hash = "sha256:4b02e52a624336eece99c96e3ab7111f469c24ba226a53ec474e8e787b365814"}, {file = "virtualenv-20.10.0.tar.gz", hash = "sha256:576d05b46eace16a9c348085f7d0dc8ef28713a2cabaa1cf0aea41e8f12c9218"}, ] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] zipp = [ {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, diff --git a/pyproject.toml b/pyproject.toml index 59400e84..c92c8157 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ pytest-depends = "^1.0.1" pytest-asyncio = "^0.16.0" Faker = "^9.3.1" unasync-cli = "^0.0.9" +commitizen = "^2.20.0" [build-system] requires = ["poetry-core>=1.0.0"] From 2f6bcf2f85f7831319c0c86faf16027a02e50058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Sat, 20 Nov 2021 00:54:55 +0000 Subject: [PATCH 02/12] feat: add job to github action for auto deploy --- .github/workflows/ci.yml | 41 +++++++++++++++++++++++++++++++++++++++- pyproject.toml | 9 +++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1984d4f4..d25e3538 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI +name: CI/CD on: [pull_request, push, workflow_dispatch] @@ -30,3 +30,42 @@ jobs: run: make tests - name: Upload Coverage uses: codecov/codecov-action@v1 + + publish: + needs: test + if: ${{ !startsWith(github.event.head_commit.message, 'bump:') && github.ref == 'refs/heads/main' && github.event_name == 'push' }} + runs-on: ubuntu-latest + name: "Bump version, create changelog and publish" + steps: + - name: Clone Repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Create bump and changelog + uses: commitizen-tools/commitizen-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: main + changelog_increment_filename: body.md + - name: Release + uses: softprops/action-gh-release@v1 + with: + body_path: body.md + tag_name: ${{ env.REVISION }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Set up Poetry + uses: abatilo/actions-poetry@v2.1.4 + with: + poetry-version: 1.1.11 + # - name: Publish + # env: + # PYPI_USERNAME: __token__ + # PYPI_PASSWORD: ${{ secrets.PYPI_TOKEN }} + # run: | + # poetry install + # poetry publish --build -u $PYPI_USERNAME -p $PYPI_PASSWORD diff --git a/pyproject.toml b/pyproject.toml index c92c8157..59563a9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,15 @@ Faker = "^9.3.1" unasync-cli = "^0.0.9" commitizen = "^2.20.0" +[tool.commitizen] +name = "cz_conventional_commits" +version = "0.2.0" +version_files = [ + "gotrue/__init__.py", + "pyproject.toml:version" +] +tag_format = "v$version" + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" From 4ef96ce77861b62aec345f443af1e7b8710df83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Thu, 2 Dec 2021 03:27:01 +0000 Subject: [PATCH 03/12] fix: get recovery mode before notify sign in event https://github.com/supabase/gotrue-js/commit/9c0f42b50e60fac00bf52c30ad3548906cf49b0a --- gotrue/_async/client.py | 3 ++- gotrue/_sync/client.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gotrue/_async/client.py b/gotrue/_async/client.py index cf23a931..b9bb77f1 100644 --- a/gotrue/_async/client.py +++ b/gotrue/_async/client.py @@ -431,8 +431,9 @@ async def get_session_from_url( ) if store_session: await self._save_session(session=session) + recovery_mode = query.get("type") self._notify_all_subscribers(event=AuthChangeEvent.SIGNED_IN) - if query.get("type") == "recovery": + if recovery_mode == "recovery": self._notify_all_subscribers(event=AuthChangeEvent.PASSWORD_RECOVERY) return session diff --git a/gotrue/_sync/client.py b/gotrue/_sync/client.py index 9fa9ee47..0940ee50 100644 --- a/gotrue/_sync/client.py +++ b/gotrue/_sync/client.py @@ -429,8 +429,9 @@ def get_session_from_url(self, *, url: str, store_session: bool = False) -> Sess ) if store_session: self._save_session(session=session) + recovery_mode = query.get("type") self._notify_all_subscribers(event=AuthChangeEvent.SIGNED_IN) - if query.get("type") == "recovery": + if recovery_mode == "recovery": self._notify_all_subscribers(event=AuthChangeEvent.PASSWORD_RECOVERY) return session From e7028356cda625fff435c69d9772c85c0b072b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Thu, 2 Dec 2021 03:30:58 +0000 Subject: [PATCH 04/12] fix: add a new TOKEN_REFRESHED event https://github.com/supabase/gotrue-js/commit/0add6956a22c51c785d9d735edf38cac8a2c2368 --- gotrue/_async/client.py | 1 + gotrue/_sync/client.py | 1 + gotrue/types.py | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/gotrue/_async/client.py b/gotrue/_async/client.py index b9bb77f1..509cc32b 100644 --- a/gotrue/_async/client.py +++ b/gotrue/_async/client.py @@ -582,6 +582,7 @@ async def _call_refresh_token( refresh_token=cast(str, refresh_token) ) await self._save_session(session=response) + self._notify_all_subscribers(event=AuthChangeEvent.TOKEN_REFRESHED) self._notify_all_subscribers(event=AuthChangeEvent.SIGNED_IN) return response diff --git a/gotrue/_sync/client.py b/gotrue/_sync/client.py index 0940ee50..ecf4c901 100644 --- a/gotrue/_sync/client.py +++ b/gotrue/_sync/client.py @@ -576,6 +576,7 @@ def _call_refresh_token(self, *, refresh_token: Optional[str] = None) -> Session raise ValueError("No current session and refresh_token not supplied.") response = self.api.refresh_access_token(refresh_token=cast(str, refresh_token)) self._save_session(session=response) + self._notify_all_subscribers(event=AuthChangeEvent.TOKEN_REFRESHED) self._notify_all_subscribers(event=AuthChangeEvent.SIGNED_IN) return response diff --git a/gotrue/types.py b/gotrue/types.py index de62adf3..2e7ea242 100644 --- a/gotrue/types.py +++ b/gotrue/types.py @@ -112,11 +112,12 @@ def validator(cls, values: dict) -> dict: class AuthChangeEvent(str, Enum): + PASSWORD_RECOVERY = "PASSWORD_RECOVERY" SIGNED_IN = "SIGNED_IN" SIGNED_OUT = "SIGNED_OUT" + TOKEN_REFRESHED = "TOKEN_REFRESHED" USER_UPDATED = "USER_UPDATED" USER_DELETED = "USER_DELETED" - PASSWORD_RECOVERY = "PASSWORD_RECOVERY" class Subscription(BaseModelFromResponse): From 367a09bb59180872667b8b213c4405733eb95998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Thu, 2 Dec 2021 04:03:27 +0000 Subject: [PATCH 05/12] test: increases coverage https://github.com/supabase/gotrue-js/commit/98300a0717c36197484873e53befc5fd25c57772 --- .../test_api_with_auto_confirm_enabled.py | 53 +++++++++++++++++++ .../test_api_with_auto_confirm_enabled.py | 53 +++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 tests/_async/test_api_with_auto_confirm_enabled.py create mode 100644 tests/_sync/test_api_with_auto_confirm_enabled.py diff --git a/tests/_async/test_api_with_auto_confirm_enabled.py b/tests/_async/test_api_with_auto_confirm_enabled.py new file mode 100644 index 00000000..0dd4d51b --- /dev/null +++ b/tests/_async/test_api_with_auto_confirm_enabled.py @@ -0,0 +1,53 @@ +from typing import AsyncIterable, Optional + +import pytest +from faker import Faker +from gotrue import AsyncGoTrueAPI +from gotrue.constants import COOKIE_OPTIONS +from gotrue.types import CookieOptions, Session, User + +GOTRUE_URL = "http://localhost:9998" +TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6InN1cGFiYXNlX2FkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ.0sOtTSTfPv5oPZxsjvBO249FI4S4p0ymHoIZ6H6z9Y8" # noqa: E501 + + +@pytest.fixture(name="api") +async def create_api() -> AsyncIterable[AsyncGoTrueAPI]: + async with AsyncGoTrueAPI( + url=GOTRUE_URL, + headers={"Authorization": f"Bearer {TOKEN}"}, + cookie_options=CookieOptions.parse_obj(COOKIE_OPTIONS), + ) as api: + yield api + + +fake = Faker() + +email = f"api_ac_enabled_{fake.email().lower()}" +password = fake.password() +valid_session: Optional[Session] = None + + +@pytest.mark.asyncio +async def test_sign_up_with_email(api: AsyncGoTrueAPI): + global valid_session + try: + response = await api.sign_up_with_email( + email=email, + password=password, + data={"status": "alpha"}, + ) + assert isinstance(response, Session) + valid_session = response + except Exception as e: + assert False, str(e) + + +@pytest.mark.asyncio +@pytest.mark.depends(on=[test_sign_up_with_email.__name__]) +async def test_get_user(api: AsyncGoTrueAPI): + try: + jwt = valid_session.access_token if valid_session else "" + response = await api.get_user(jwt=jwt) + assert isinstance(response, User) + except Exception as e: + assert False, str(e) diff --git a/tests/_sync/test_api_with_auto_confirm_enabled.py b/tests/_sync/test_api_with_auto_confirm_enabled.py new file mode 100644 index 00000000..62e5eef0 --- /dev/null +++ b/tests/_sync/test_api_with_auto_confirm_enabled.py @@ -0,0 +1,53 @@ +from typing import Iterable, Optional + +import pytest +from faker import Faker +from gotrue import SyncGoTrueAPI +from gotrue.constants import COOKIE_OPTIONS +from gotrue.types import CookieOptions, Session, User + +GOTRUE_URL = "http://localhost:9998" +TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6InN1cGFiYXNlX2FkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ.0sOtTSTfPv5oPZxsjvBO249FI4S4p0ymHoIZ6H6z9Y8" # noqa: E501 + + +@pytest.fixture(name="api") +def create_api() -> Iterable[SyncGoTrueAPI]: + with SyncGoTrueAPI( + url=GOTRUE_URL, + headers={"Authorization": f"Bearer {TOKEN}"}, + cookie_options=CookieOptions.parse_obj(COOKIE_OPTIONS), + ) as api: + yield api + + +fake = Faker() + +email = f"api_ac_enabled_{fake.email().lower()}" +password = fake.password() +valid_session: Optional[Session] = None + + +@pytest.mark.asyncio +def test_sign_up_with_email(api: SyncGoTrueAPI): + global valid_session + try: + response = api.sign_up_with_email( + email=email, + password=password, + data={"status": "alpha"}, + ) + assert isinstance(response, Session) + valid_session = response + except Exception as e: + assert False, str(e) + + +@pytest.mark.asyncio +@pytest.mark.depends(on=[test_sign_up_with_email.__name__]) +def test_get_user(api: SyncGoTrueAPI): + try: + jwt = valid_session.access_token if valid_session else "" + response = api.get_user(jwt=jwt) + assert isinstance(response, User) + except Exception as e: + assert False, str(e) From 4ec0e075cafd9e72369afb9c8313a71923455489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Thu, 2 Dec 2021 04:04:09 +0000 Subject: [PATCH 06/12] test: change message https://github.com/supabase/gotrue/commit/92abe187fd3d96645331542b477b37487cafce07 --- tests/_async/test_api_with_auto_confirm_enabled.py | 1 + tests/_async/test_client_with_auto_confirm_disabled.py | 2 +- tests/_sync/test_api_with_auto_confirm_enabled.py | 1 + tests/_sync/test_client_with_auto_confirm_disabled.py | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/_async/test_api_with_auto_confirm_enabled.py b/tests/_async/test_api_with_auto_confirm_enabled.py index 0dd4d51b..e0852010 100644 --- a/tests/_async/test_api_with_auto_confirm_enabled.py +++ b/tests/_async/test_api_with_auto_confirm_enabled.py @@ -2,6 +2,7 @@ import pytest from faker import Faker + from gotrue import AsyncGoTrueAPI from gotrue.constants import COOKIE_OPTIONS from gotrue.types import CookieOptions, Session, User diff --git a/tests/_async/test_client_with_auto_confirm_disabled.py b/tests/_async/test_client_with_auto_confirm_disabled.py index 1493bca4..8525118d 100644 --- a/tests/_async/test_client_with_auto_confirm_disabled.py +++ b/tests/_async/test_client_with_auto_confirm_disabled.py @@ -81,7 +81,7 @@ async def test_sign_in(client: AsyncGoTrueClient): @pytest.mark.asyncio @pytest.mark.depends(on=[test_sign_up_with_email_and_password.__name__]) async def test_sign_in_with_the_wrong_password(client: AsyncGoTrueClient): - expected_error_message = "Email not confirmed" + expected_error_message = "Invalid login credentials" try: await client.sign_in( email=email, diff --git a/tests/_sync/test_api_with_auto_confirm_enabled.py b/tests/_sync/test_api_with_auto_confirm_enabled.py index 62e5eef0..cab638e5 100644 --- a/tests/_sync/test_api_with_auto_confirm_enabled.py +++ b/tests/_sync/test_api_with_auto_confirm_enabled.py @@ -2,6 +2,7 @@ import pytest from faker import Faker + from gotrue import SyncGoTrueAPI from gotrue.constants import COOKIE_OPTIONS from gotrue.types import CookieOptions, Session, User diff --git a/tests/_sync/test_client_with_auto_confirm_disabled.py b/tests/_sync/test_client_with_auto_confirm_disabled.py index 5439ad9d..be4c64a0 100644 --- a/tests/_sync/test_client_with_auto_confirm_disabled.py +++ b/tests/_sync/test_client_with_auto_confirm_disabled.py @@ -81,7 +81,7 @@ def test_sign_in(client: SyncGoTrueClient): @pytest.mark.asyncio @pytest.mark.depends(on=[test_sign_up_with_email_and_password.__name__]) def test_sign_in_with_the_wrong_password(client: SyncGoTrueClient): - expected_error_message = "Email not confirmed" + expected_error_message = "Invalid login credentials" try: client.sign_in( email=email, From 26783b583b28be8c35eb0d865242a60bd0567973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Thu, 2 Dec 2021 18:42:19 +0000 Subject: [PATCH 07/12] chore: add html report to make rul --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f01e6533..ac1b6af7 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ tests_pre_commit: poetry run pre-commit run --all-files tests_only: - poetry run pytest --cov=./ --cov-report=xml -vv + poetry run pytest --cov=./ --cov-report=xml --cov-report=html -vv run_infra: cd infra &&\ From a2ca372434865675afc46582c4ca919e50fab72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Thu, 2 Dec 2021 18:42:55 +0000 Subject: [PATCH 08/12] test: add more tests to client layer --- .../test_client_with_auto_confirm_enabled.py | 80 +++++++++++++++++++ .../test_client_with_auto_confirm_enabled.py | 80 +++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/tests/_async/test_client_with_auto_confirm_enabled.py b/tests/_async/test_client_with_auto_confirm_enabled.py index 015c54b2..486cc6c9 100644 --- a/tests/_async/test_client_with_auto_confirm_enabled.py +++ b/tests/_async/test_client_with_auto_confirm_enabled.py @@ -206,6 +206,21 @@ async def test_get_user(client: AsyncGoTrueClient): assert False, str(e) +@pytest.mark.asyncio +@pytest.mark.depends(on=[test_sign_in.__name__]) +async def test_get_session(client: AsyncGoTrueClient): + try: + await client.init_recover() + response = client.session() + assert isinstance(response, Session) + assert response.access_token + assert response.refresh_token + assert response.expires_in + assert response.expires_at + except Exception as e: + assert False, str(e) + + @pytest.mark.asyncio @pytest.mark.depends(on=[test_sign_in.__name__]) async def test_update_user(client: AsyncGoTrueClient): @@ -250,7 +265,10 @@ async def test_get_user_after_update(client: AsyncGoTrueClient): @pytest.mark.depends(on=[test_get_user_after_update.__name__]) async def test_sign_out(client: AsyncGoTrueClient): try: + await client.init_recover() await client.sign_out() + response = client.session() + assert response is None except Exception as e: assert False, str(e) @@ -266,6 +284,20 @@ async def test_get_user_after_sign_out(client: AsyncGoTrueClient): assert False, str(e) +@pytest.mark.asyncio +@pytest.mark.depends(on=[test_sign_out.__name__]) +async def test_get_update_user_after_sign_out(client: AsyncGoTrueClient): + expected_error_message = "Not logged in." + try: + await client.init_recover() + await client.update(attributes=UserAttributes(data={"hello": "world"})) + assert False + except ValueError as e: + assert str(e) == expected_error_message + except Exception as e: + assert False, str(e) + + @pytest.mark.asyncio @pytest.mark.depends(on=[test_get_user_after_sign_out.__name__]) async def test_sign_in_with_the_wrong_password(client: AsyncGoTrueClient): @@ -276,3 +308,51 @@ async def test_sign_in_with_the_wrong_password(client: AsyncGoTrueClient): assert True except Exception as e: assert False, str(e) + + +@pytest.mark.asyncio +async def test_sign_up_with_password_none(client: AsyncGoTrueClient): + expected_error_message = "Password must be defined, can't be None." + try: + await client.sign_up(email=email) + assert False + except ValueError as e: + assert str(e) == expected_error_message + except Exception as e: + assert False, str(e) + + +@pytest.mark.asyncio +async def test_sign_up_with_email_and_phone_none(client: AsyncGoTrueClient): + expected_error_message = "Email or phone must be defined, both can't be None." + try: + await client.sign_up(password=password) + assert False + except ValueError as e: + assert str(e) == expected_error_message + except Exception as e: + assert False, str(e) + + +@pytest.mark.asyncio +async def test_sign_in_with_all_nones(client: AsyncGoTrueClient): + expected_error_message = ( + "Email, phone, refresh_token, or provider must be defined, " + "all can't be None." + ) + try: + await client.sign_in() + assert False + except ValueError as e: + assert str(e) == expected_error_message + except Exception as e: + assert False, str(e) + + +@pytest.mark.asyncio +async def test_sign_in_with_magic_link(client: AsyncGoTrueClient): + try: + response = await client.sign_in(email=email) + assert response is None + except Exception as e: + assert False, str(e) diff --git a/tests/_sync/test_client_with_auto_confirm_enabled.py b/tests/_sync/test_client_with_auto_confirm_enabled.py index 672af6d6..21ed348a 100644 --- a/tests/_sync/test_client_with_auto_confirm_enabled.py +++ b/tests/_sync/test_client_with_auto_confirm_enabled.py @@ -202,6 +202,21 @@ def test_get_user(client: SyncGoTrueClient): assert False, str(e) +@pytest.mark.asyncio +@pytest.mark.depends(on=[test_sign_in.__name__]) +def test_get_session(client: SyncGoTrueClient): + try: + client.init_recover() + response = client.session() + assert isinstance(response, Session) + assert response.access_token + assert response.refresh_token + assert response.expires_in + assert response.expires_at + except Exception as e: + assert False, str(e) + + @pytest.mark.asyncio @pytest.mark.depends(on=[test_sign_in.__name__]) def test_update_user(client: SyncGoTrueClient): @@ -244,7 +259,10 @@ def test_get_user_after_update(client: SyncGoTrueClient): @pytest.mark.depends(on=[test_get_user_after_update.__name__]) def test_sign_out(client: SyncGoTrueClient): try: + client.init_recover() client.sign_out() + response = client.session() + assert response is None except Exception as e: assert False, str(e) @@ -260,6 +278,20 @@ def test_get_user_after_sign_out(client: SyncGoTrueClient): assert False, str(e) +@pytest.mark.asyncio +@pytest.mark.depends(on=[test_sign_out.__name__]) +def test_get_update_user_after_sign_out(client: SyncGoTrueClient): + expected_error_message = "Not logged in." + try: + client.init_recover() + client.update(attributes=UserAttributes(data={"hello": "world"})) + assert False + except ValueError as e: + assert str(e) == expected_error_message + except Exception as e: + assert False, str(e) + + @pytest.mark.asyncio @pytest.mark.depends(on=[test_get_user_after_sign_out.__name__]) def test_sign_in_with_the_wrong_password(client: SyncGoTrueClient): @@ -270,3 +302,51 @@ def test_sign_in_with_the_wrong_password(client: SyncGoTrueClient): assert True except Exception as e: assert False, str(e) + + +@pytest.mark.asyncio +def test_sign_up_with_password_none(client: SyncGoTrueClient): + expected_error_message = "Password must be defined, can't be None." + try: + client.sign_up(email=email) + assert False + except ValueError as e: + assert str(e) == expected_error_message + except Exception as e: + assert False, str(e) + + +@pytest.mark.asyncio +def test_sign_up_with_email_and_phone_none(client: SyncGoTrueClient): + expected_error_message = "Email or phone must be defined, both can't be None." + try: + client.sign_up(password=password) + assert False + except ValueError as e: + assert str(e) == expected_error_message + except Exception as e: + assert False, str(e) + + +@pytest.mark.asyncio +def test_sign_in_with_all_nones(client: SyncGoTrueClient): + expected_error_message = ( + "Email, phone, refresh_token, or provider must be defined, " + "all can't be None." + ) + try: + client.sign_in() + assert False + except ValueError as e: + assert str(e) == expected_error_message + except Exception as e: + assert False, str(e) + + +@pytest.mark.asyncio +def test_sign_in_with_magic_link(client: SyncGoTrueClient): + try: + response = client.sign_in(email=email) + assert response is None + except Exception as e: + assert False, str(e) From f717a9f45bcf5f7a1310d028f35cb528e7ce3404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Thu, 2 Dec 2021 18:52:21 +0000 Subject: [PATCH 09/12] chore: remove html report for try fix the gh action --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ac1b6af7..f01e6533 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ tests_pre_commit: poetry run pre-commit run --all-files tests_only: - poetry run pytest --cov=./ --cov-report=xml --cov-report=html -vv + poetry run pytest --cov=./ --cov-report=xml -vv run_infra: cd infra &&\ From e4a1405bbc101ecdb5c9d3f5cbe1b38f82b8fadd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Thu, 2 Dec 2021 19:42:46 +0000 Subject: [PATCH 10/12] chore: add make rules for generate html report and clean infra --- Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f01e6533..2f3c994b 100644 --- a/Makefile +++ b/Makefile @@ -12,13 +12,18 @@ tests_pre_commit: poetry run pre-commit run --all-files tests_only: - poetry run pytest --cov=./ --cov-report=xml -vv + poetry run pytest --cov=./ --cov-report=xml --cov-report=html -vv run_infra: cd infra &&\ docker-compose down &&\ docker-compose up -d +clean_infra: + cd infra &&\ + docker-compose down &&\ + docker system prune -a --volumes -f + run_tests: run_infra tests build_sync: From 53609677568216e2eae789d4416a5848269cdd0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Fri, 3 Dec 2021 12:57:06 +0000 Subject: [PATCH 11/12] test: fix docker-compose and perf ci --- .github/workflows/ci.yml | 7 +------ Makefile | 5 ++++- infra/docker-compose.yml | 3 ++- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d25e3538..f5fdc4fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,6 @@ jobs: os: [ubuntu-latest] python-version: [3.7, 3.8, 3.9, '3.10'] runs-on: ${{ matrix.os }} - # TODO(fedden): We need to discuss these steps: We could just use a test-supabase instance or we could update the docker image and use that for the tests. steps: - name: Clone Repository uses: actions/checkout@v2 @@ -22,12 +21,8 @@ jobs: uses: abatilo/actions-poetry@v2.1.4 with: poetry-version: 1.1.11 - - name: Build Docker image - run: make run_infra - - name: Sleep for 5 seconds - run: sleep 5 - name: Run Tests - run: make tests + run: make run_tests - name: Upload Coverage uses: codecov/codecov-action@v1 diff --git a/Makefile b/Makefile index 2f3c994b..f65707e0 100644 --- a/Makefile +++ b/Makefile @@ -24,10 +24,13 @@ clean_infra: docker-compose down &&\ docker system prune -a --volumes -f -run_tests: run_infra tests +run_tests: run_infra sleep tests build_sync: poetry run unasync gotrue tests build_run_tests: build_sync run_tests echo "Done" + +sleep: + sleep 10 diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index b9ac2ea4..a83a1dcf 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -106,9 +106,10 @@ services: - '9000:9000' # web interface - '1100:1100' # POP3 db: - image: supabase/postgres + image: supabase/postgres:latest ports: - '5432:5432' + command: postgres -c config_file=/etc/postgresql/postgresql.conf volumes: - ./db:/docker-entrypoint-initdb.d/ environment: From 5f65e472174eedbd0fb211ffa769cc21d6886b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leynier=20Guti=C3=A9rrez=20Gonz=C3=A1lez?= Date: Fri, 3 Dec 2021 13:46:12 +0000 Subject: [PATCH 12/12] fix: error in get_session_from_url --- gotrue/_async/client.py | 15 +++++++++------ gotrue/_sync/client.py | 17 +++++++++++------ .../test_client_with_auto_confirm_enabled.py | 19 +++++++++++++++++++ .../test_client_with_auto_confirm_enabled.py | 19 +++++++++++++++++++ 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/gotrue/_async/client.py b/gotrue/_async/client.py index 509cc32b..30b48661 100644 --- a/gotrue/_async/client.py +++ b/gotrue/_async/client.py @@ -376,7 +376,10 @@ async def set_auth(self, *, access_token: str) -> Session: return session async def get_session_from_url( - self, *, url: str, store_session: bool = False + self, + *, + url: str, + store_session: bool = False, ) -> Session: """Gets the session data from a URL string. @@ -406,16 +409,16 @@ async def get_session_from_url( token_type = query.get("token_type") if error_description: raise APIError(error_description[0], 400) - if not access_token: + if not access_token or not access_token[0]: raise APIError("No access_token detected.", 400) - if not expires_in or expires_in[0]: + if not expires_in or not expires_in[0]: raise APIError("No expires_in detected.", 400) - if not refresh_token: + if not refresh_token or not refresh_token[0]: raise APIError("No refresh_token detected.", 400) - if not token_type: + if not token_type or not token_type[0]: raise APIError("No token_type detected.", 400) try: - expires_at = round(time.time()) + int(expires_in[0]) + expires_at = round(time()) + int(expires_in[0]) except ValueError: raise APIError("Invalid expires_in.", 400) response = await self.api.get_user(jwt=access_token[0]) diff --git a/gotrue/_sync/client.py b/gotrue/_sync/client.py index ecf4c901..48dd3bd9 100644 --- a/gotrue/_sync/client.py +++ b/gotrue/_sync/client.py @@ -375,7 +375,12 @@ def set_auth(self, *, access_token: str) -> Session: self._save_session(session=session) return session - def get_session_from_url(self, *, url: str, store_session: bool = False) -> Session: + def get_session_from_url( + self, + *, + url: str, + store_session: bool = False, + ) -> Session: """Gets the session data from a URL string. Parameters @@ -404,16 +409,16 @@ def get_session_from_url(self, *, url: str, store_session: bool = False) -> Sess token_type = query.get("token_type") if error_description: raise APIError(error_description[0], 400) - if not access_token: + if not access_token or not access_token[0]: raise APIError("No access_token detected.", 400) - if not expires_in or expires_in[0]: + if not expires_in or not expires_in[0]: raise APIError("No expires_in detected.", 400) - if not refresh_token: + if not refresh_token or not refresh_token[0]: raise APIError("No refresh_token detected.", 400) - if not token_type: + if not token_type or not token_type[0]: raise APIError("No token_type detected.", 400) try: - expires_at = round(time.time()) + int(expires_in[0]) + expires_at = round(time()) + int(expires_in[0]) except ValueError: raise APIError("Invalid expires_in.", 400) response = self.api.get_user(jwt=access_token[0]) diff --git a/tests/_async/test_client_with_auto_confirm_enabled.py b/tests/_async/test_client_with_auto_confirm_enabled.py index 486cc6c9..f9d4f708 100644 --- a/tests/_async/test_client_with_auto_confirm_enabled.py +++ b/tests/_async/test_client_with_auto_confirm_enabled.py @@ -356,3 +356,22 @@ async def test_sign_in_with_magic_link(client: AsyncGoTrueClient): assert response is None except Exception as e: assert False, str(e) + + +@pytest.mark.asyncio +@pytest.mark.depends(on=[test_sign_up.__name__]) +async def test_get_session_from_url(client: AsyncGoTrueClient): + try: + assert access_token + dummy_url = ( + "https://localhost" + f"?access_token={access_token}" + "&refresh_token=refresh_token" + "&token_type=bearer" + "&expires_in=3600" + "&type=recovery" + ) + response = await client.get_session_from_url(url=dummy_url, store_session=True) + assert isinstance(response, Session) + except Exception as e: + assert False, str(e) diff --git a/tests/_sync/test_client_with_auto_confirm_enabled.py b/tests/_sync/test_client_with_auto_confirm_enabled.py index 21ed348a..7b1d4e46 100644 --- a/tests/_sync/test_client_with_auto_confirm_enabled.py +++ b/tests/_sync/test_client_with_auto_confirm_enabled.py @@ -350,3 +350,22 @@ def test_sign_in_with_magic_link(client: SyncGoTrueClient): assert response is None except Exception as e: assert False, str(e) + + +@pytest.mark.asyncio +@pytest.mark.depends(on=[test_sign_up.__name__]) +def test_get_session_from_url(client: SyncGoTrueClient): + try: + assert access_token + dummy_url = ( + "https://localhost" + f"?access_token={access_token}" + "&refresh_token=refresh_token" + "&token_type=bearer" + "&expires_in=3600" + "&type=recovery" + ) + response = client.get_session_from_url(url=dummy_url, store_session=True) + assert isinstance(response, Session) + except Exception as e: + assert False, str(e)