Files
go-garth/garth/tests/test_http.py
2025-09-07 06:38:39 -07:00

279 lines
8.0 KiB
Python

import tempfile
import time
from typing import Any, cast
import pytest
from requests.adapters import HTTPAdapter
from garth.auth_tokens import OAuth1Token, OAuth2Token
from garth.exc import GarthHTTPError
from garth.http import Client
def test_dump_and_load(authed_client: Client):
with tempfile.TemporaryDirectory() as tempdir:
authed_client.dump(tempdir)
new_client = Client()
new_client.load(tempdir)
assert new_client.oauth1_token == authed_client.oauth1_token
assert new_client.oauth2_token == authed_client.oauth2_token
def test_dumps_and_loads(authed_client: Client):
s = authed_client.dumps()
new_client = Client()
new_client.loads(s)
assert new_client.oauth1_token == authed_client.oauth1_token
assert new_client.oauth2_token == authed_client.oauth2_token
def test_configure_oauth2_token(client: Client, oauth2_token: OAuth2Token):
assert client.oauth2_token is None
client.configure(oauth2_token=oauth2_token)
assert client.oauth2_token == oauth2_token
def test_configure_domain(client: Client):
assert client.domain == "garmin.com"
client.configure(domain="garmin.cn")
assert client.domain == "garmin.cn"
def test_configure_proxies(client: Client):
assert client.sess.proxies == {}
proxy = {"https": "http://localhost:8888"}
client.configure(proxies=proxy)
assert client.sess.proxies["https"] == proxy["https"]
def test_configure_ssl_verify(client: Client):
assert client.sess.verify is True
client.configure(ssl_verify=False)
assert client.sess.verify is False
def test_configure_timeout(client: Client):
assert client.timeout == 10
client.configure(timeout=99)
assert client.timeout == 99
def test_configure_retry(client: Client):
assert client.retries == 3
adapter = client.sess.adapters["https://"]
assert isinstance(adapter, HTTPAdapter)
assert adapter.max_retries.total == client.retries
client.configure(retries=99)
assert client.retries == 99
adapter = client.sess.adapters["https://"]
assert isinstance(adapter, HTTPAdapter)
assert adapter.max_retries.total == 99
def test_configure_status_forcelist(client: Client):
assert client.status_forcelist == (408, 429, 500, 502, 503, 504)
adapter = client.sess.adapters["https://"]
assert isinstance(adapter, HTTPAdapter)
assert adapter.max_retries.status_forcelist == client.status_forcelist
client.configure(status_forcelist=(200, 201, 202))
assert client.status_forcelist == (200, 201, 202)
adapter = client.sess.adapters["https://"]
assert isinstance(adapter, HTTPAdapter)
assert adapter.max_retries.status_forcelist == client.status_forcelist
def test_configure_backoff_factor(client: Client):
assert client.backoff_factor == 0.5
adapter = client.sess.adapters["https://"]
assert isinstance(adapter, HTTPAdapter)
assert adapter.max_retries.backoff_factor == client.backoff_factor
client.configure(backoff_factor=0.99)
assert client.backoff_factor == 0.99
adapter = client.sess.adapters["https://"]
assert isinstance(adapter, HTTPAdapter)
assert adapter.max_retries.backoff_factor == client.backoff_factor
def test_configure_pool_maxsize(client: Client):
assert client.pool_maxsize == 10
client.configure(pool_maxsize=99)
assert client.pool_maxsize == 99
adapter = client.sess.adapters["https://"]
assert isinstance(adapter, HTTPAdapter)
assert adapter.poolmanager.connection_pool_kw["maxsize"] == 99
def test_configure_pool_connections(client: Client):
client.configure(pool_connections=99)
assert client.pool_connections == 99
adapter = client.sess.adapters["https://"]
assert isinstance(adapter, HTTPAdapter)
assert getattr(adapter, "_pool_connections", None) == 99, (
"Pool connections not properly configured"
)
@pytest.mark.vcr
def test_client_request(client: Client):
resp = client.request("GET", "connect", "/")
assert resp.ok
with pytest.raises(GarthHTTPError) as e:
client.request("GET", "connectapi", "/")
assert "404" in str(e.value)
@pytest.mark.vcr
def test_login_success_mfa(monkeypatch, client: Client):
def mock_input(_):
return "327751"
monkeypatch.setattr("builtins.input", mock_input)
assert client.oauth1_token is None
assert client.oauth2_token is None
client.login("user@example.com", "correct_password")
assert client.oauth1_token
assert client.oauth2_token
@pytest.mark.vcr
def test_username(authed_client: Client):
assert authed_client._user_profile is None
assert authed_client.username
assert authed_client._user_profile
@pytest.mark.vcr
def test_profile_alias(authed_client: Client):
assert authed_client._user_profile is None
profile = authed_client.profile
assert profile == authed_client.user_profile
assert authed_client._user_profile is not None
@pytest.mark.vcr
def test_connectapi(authed_client: Client):
stress = cast(
list[dict[str, Any]],
authed_client.connectapi(
"/usersummary-service/stats/stress/daily/2023-07-21/2023-07-21"
),
)
assert stress
assert isinstance(stress, list)
assert len(stress) == 1
assert stress[0]["calendarDate"] == "2023-07-21"
assert list(stress[0]["values"].keys()) == [
"highStressDuration",
"lowStressDuration",
"overallStressLevel",
"restStressDuration",
"mediumStressDuration",
]
@pytest.mark.vcr
def test_refresh_oauth2_token(authed_client: Client):
assert authed_client.oauth2_token and isinstance(
authed_client.oauth2_token, OAuth2Token
)
authed_client.oauth2_token.expires_at = int(time.time())
assert authed_client.oauth2_token.expired
profile = authed_client.connectapi("/userprofile-service/socialProfile")
assert profile
assert isinstance(profile, dict)
assert profile["userName"]
@pytest.mark.vcr
def test_download(authed_client: Client):
downloaded = authed_client.download(
"/download-service/files/activity/11998957007"
)
assert downloaded
zip_magic_number = b"\x50\x4b\x03\x04"
assert downloaded[:4] == zip_magic_number
@pytest.mark.vcr
def test_upload(authed_client: Client):
fpath = "tests/12129115726_ACTIVITY.fit"
with open(fpath, "rb") as f:
uploaded = authed_client.upload(f)
assert uploaded
@pytest.mark.vcr
def test_delete(authed_client: Client):
activity_id = "12135235656"
path = f"/activity-service/activity/{activity_id}"
assert authed_client.connectapi(path)
authed_client.delete(
"connectapi",
path,
api=True,
)
with pytest.raises(GarthHTTPError) as e:
authed_client.connectapi(path)
assert "404" in str(e.value)
@pytest.mark.vcr
def test_put(authed_client: Client):
data = [
{
"changeState": "CHANGED",
"trainingMethod": "HR_RESERVE",
"lactateThresholdHeartRateUsed": 170,
"maxHeartRateUsed": 185,
"restingHrAutoUpdateUsed": False,
"sport": "DEFAULT",
"zone1Floor": 130,
"zone2Floor": 140,
"zone3Floor": 150,
"zone4Floor": 160,
"zone5Floor": 170,
}
]
path = "/biometric-service/heartRateZones"
authed_client.put(
"connectapi",
path,
api=True,
json=data,
)
assert authed_client.connectapi(path)
@pytest.mark.vcr
def test_resume_login(client: Client):
result = client.login(
"example@example.com",
"correct_password",
return_on_mfa=True,
)
assert isinstance(result, tuple)
result_type, client_state = result
assert isinstance(client_state, dict)
assert result_type == "needs_mfa"
assert "signin_params" in client_state
assert "client" in client_state
code = "123456" # obtain from custom login
# test resuming the login
oauth1, oauth2 = client.resume_login(client_state, code)
assert oauth1
assert isinstance(oauth1, OAuth1Token)
assert oauth2
assert isinstance(oauth2, OAuth2Token)