Skip to content

Commit

Permalink
mob next [ci-skip] [ci skip] [skip ci]
Browse files Browse the repository at this point in the history
lastFile:skore/tests/unit/hub/test_login.py
  • Loading branch information
thomass-dev committed Feb 25, 2025
1 parent 75734a5 commit 993d9f7
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 130 deletions.
4 changes: 2 additions & 2 deletions skore/src/skore/hub/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from httpx import URL, Client, Response

from skore.hub import api
from skore.hub.api import URI
from skore.hub.token import AuthenticationToken


Expand Down Expand Up @@ -38,7 +38,7 @@ def request(self, method: str, url: URL | str, **kwargs) -> Response:

return super().request(
method,
urljoin(api.URI, str(url)),
urljoin(URI, str(url)),
headers=headers,
**kwargs,
)
1 change: 1 addition & 0 deletions skore/src/skore/hub/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

def login(timeout=600, auto_otp=True):
"""Login to the skore-HUB."""

if auto_otp:
access = None
refreshment = None
Expand Down
4 changes: 2 additions & 2 deletions skore/src/skore/hub/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from datetime import datetime
from typing import Optional

from skore.hub import api
from skore.hub.api import post_oauth_refresh_token


class AuthenticationToken:
Expand Down Expand Up @@ -57,7 +57,7 @@ def save(self):

def refresh(self):
"""Call the API to get a fresh access token."""
content = api.post_oauth_refresh_token(self.refreshment)
content = post_oauth_refresh_token(self.refreshment)

self.access, self.refreshment = content[:2]
self.expires_at = datetime.fromisoformat(content[2])
Expand Down
127 changes: 1 addition & 126 deletions skore/tests/unit/hub/test_client.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import time
from datetime import datetime
from threading import Thread
from urllib.parse import urljoin

import httpx
import pytest
from httpx import Response
from skore.hub.api import URI
from skore.hub.client import AuthenticatedClient, AuthenticationError, login
from skore.hub.client import AuthenticatedClient, AuthenticationError


CALLBACK_URL = urljoin(URI, "identity/oauth/device/callback")
LOGIN_URL = urljoin(URI, "identity/oauth/device/login")
REFRESH_URL = urljoin(URI, "identity/oauth/token/refresh")
TOKEN_URL = urljoin(URI, "identity/oauth/device/token")


class TestAuthenticatedClient:
Expand Down Expand Up @@ -69,122 +63,3 @@ def test_request_with_expired_token(
assert client.token.access == "D"
assert client.token.refreshment == "E"
assert client.token.expires_at == mock_now


@pytest.mark.respx(assert_all_called=True)
def test_manual_login(monkeypatch, respx_mock, mock_now, mock_nowstr):
def webbrowser_open(url):
webbrowser_open.url = url

monkeypatch.setattr("webbrowser.open", webbrowser_open)

respx_mock.get(LOGIN_URL).mock(
Response(
200,
json={
"authorization_url": "url",
"device_code": "device",
"user_code": "user",
},
)
)
respx_mock.get(TOKEN_URL).mock(
Response(
200,
json={
"token": {
"access_token": "A",
"refresh_token": "B",
"expires_at": mock_nowstr,
}
},
)
)

token = login(auto_otp=False)

assert webbrowser_open.url == "url"
assert token.access == "A"
assert token.refreshment == "B"
assert token.expires_at == mock_now


@pytest.mark.respx(assert_all_called=True)
def test_manual_login_timeout(monkeypatch, respx_mock):
monkeypatch.setattr("webbrowser.open", lambda _: None)

respx_mock.get(TOKEN_URL).mock(Response(500))
respx_mock.get(LOGIN_URL).mock(
Response(
200,
json={
"authorization_url": "https://idp.com",
"device_code": "device-123",
"user_code": "123",
},
)
)

with pytest.raises(AuthenticationError, match="timed out"):
login(timeout=0, auto_otp=False)


@pytest.mark.respx(assert_all_called=True)
def test_auto_otp_login(monkeypatch, respx_mock, mock_now, mock_nowstr):
def webbrowser_open(url):
webbrowser_open.url = url

monkeypatch.setattr("webbrowser.open", webbrowser_open)
respx_mock.route(host="localhost").pass_through()

login_route = respx_mock.get(LOGIN_URL).mock(
Response(
200,
json={
"authorization_url": "url",
"device_code": "device",
"user_code": "user",
},
)
)
respx_mock.post(CALLBACK_URL).mock(Response(201))
respx_mock.get(TOKEN_URL).mock(
Response(
200,
json={
"token": {
"access_token": "A",
"refresh_token": "B",
"expires_at": mock_nowstr,
}
},
)
)

port = -1

def mock_create_server(callback):
from skore.hub.callback_server import launch_callback_server

nonlocal port
port = launch_callback_server(callback)
return port

monkeypatch.setattr("skore.hub.client.launch_callback_server", mock_create_server)

def call_success():
with httpx.Client() as client:
while not login_route.called:
time.sleep(0.5)

client.get(f"http://localhost:{port}")

success_thread = Thread(target=call_success)
success_thread.start()

token = login(auto_otp=True)

assert webbrowser_open.url == "url"
assert token.access == "A"
assert token.refreshment == "B"
assert token.expires_at == mock_now
134 changes: 134 additions & 0 deletions skore/tests/unit/hub/test_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import time
from threading import Thread
from urllib.parse import urljoin

import httpx
import pytest
from httpx import Response
from skore.hub.api import URI
from skore.hub.client import AuthenticationError
from skore.hub.login import login


CALLBACK_URL = urljoin(URI, "identity/oauth/device/callback")
LOGIN_URL = urljoin(URI, "identity/oauth/device/login")
TOKEN_URL = urljoin(URI, "identity/oauth/device/token")


@pytest.mark.respx(assert_all_called=True)
def test_manual_login(monkeypatch, respx_mock, mock_now, mock_nowstr):
def webbrowser_open(url):
webbrowser_open.url = url

monkeypatch.setattr("webbrowser.open", webbrowser_open)

respx_mock.get(LOGIN_URL).mock(
Response(
200,
json={
"authorization_url": "url",
"device_code": "device",
"user_code": "user",
},
)
)
respx_mock.get(TOKEN_URL).mock(
Response(
200,
json={
"token": {
"access_token": "A",
"refresh_token": "B",
"expires_at": mock_nowstr,
}
},
)
)

token = login(auto_otp=False)

assert webbrowser_open.url == "url"
assert token.access == "A"
assert token.refreshment == "B"
assert token.expires_at == mock_now


@pytest.mark.respx(assert_all_called=True)
def test_manual_login_timeout(monkeypatch, respx_mock):
monkeypatch.setattr("webbrowser.open", lambda _: None)

respx_mock.get(TOKEN_URL).mock(Response(500))
respx_mock.get(LOGIN_URL).mock(
Response(
200,
json={
"authorization_url": "https://idp.com",
"device_code": "device-123",
"user_code": "123",
},
)
)

with pytest.raises(AuthenticationError, match="Timeout"):
login(timeout=0, auto_otp=False)


@pytest.mark.respx(assert_all_called=True)
def test_auto_otp_login(monkeypatch, respx_mock, mock_now, mock_nowstr):
def webbrowser_open(url):
webbrowser_open.url = url

monkeypatch.setattr("webbrowser.open", webbrowser_open)
respx_mock.route(host="localhost").pass_through()

login_route = respx_mock.get(LOGIN_URL).mock(
Response(
200,
json={
"authorization_url": "url",
"device_code": "device",
"user_code": "user",
},
)
)
respx_mock.post(CALLBACK_URL).mock(Response(201))
respx_mock.get(TOKEN_URL).mock(
Response(
200,
json={
"token": {
"access_token": "A",
"refresh_token": "B",
"expires_at": mock_nowstr,
}
},
)
)

port = -1

def mock_create_server(callback):
from skore.hub.callback_server import launch_callback_server

nonlocal port
port = launch_callback_server(callback)
return port

monkeypatch.setattr("skore.hub.login.launch_callback_server", mock_create_server)

def call_success():
with httpx.Client() as client:
while not login_route.called:
time.sleep(0.5)

client.get(f"http://localhost:{port}")

success_thread = Thread(target=call_success)
success_thread.start()

token = login(auto_otp=True)

assert webbrowser_open.url == "url"
assert token.access == "A"
assert token.refreshment == "B"
assert token.expires_at == mock_now

0 comments on commit 993d9f7

Please sign in to comment.