aetherx-oracle 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: aetherx-oracle
3
+ Version: 0.3.0
4
+ Summary: Python SDK for the Aether-X Port Congestion Oracle API
5
+ Author: Aether-X Oracle Engine
6
+ License: Proprietary - Machine-to-Machine Data Distribution
7
+ Project-URL: Homepage, https://aether-x-oracle-production.up.railway.app
8
+ Project-URL: Documentation, https://aether-x-oracle-production.up.railway.app/docs
9
+ Project-URL: Terms, https://aether-x-oracle-production.up.railway.app/terms
10
+ Keywords: port,congestion,oracle,logistics,shipping,quant,freight
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: requests>=2.25.0
14
+ Requires-Dist: pydantic>=2.0.0
15
+ Provides-Extra: async
16
+ Requires-Dist: httpx>=0.24.0; extra == "async"
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest>=7.0; extra == "test"
19
+ Requires-Dist: responses>=0.23; extra == "test"
20
+ Requires-Dist: httpx>=0.24.0; extra == "test"
21
+
22
+ # aetherx-oracle
23
+
24
+ SDK Python oficial para a **Aether-X Port Congestion Oracle API** — sinais preditivos de congestão portuária, atraso de ETA e volatilidade de frete para portos globais.
25
+
26
+ ## Instalação
27
+
28
+ ```bash
29
+ pip install aetherx-oracle
30
+ ```
31
+
32
+ ## Uso rápido
33
+
34
+ ```python
35
+ from aetherx import OracleClient
36
+
37
+ client = OracleClient(api_key="SUA_RAPIDAPI_KEY")
38
+
39
+ risk = client.get_port_risk("BRSSZ")
40
+
41
+ print(risk.port_name) # Santos
42
+ print(risk.country) # Brasil
43
+ print(risk.congestion_score) # 0.78
44
+ print(risk.eta_delay_days) # 1.6
45
+ print(risk.waiting_vessels) # 12
46
+ print(risk.freight_volatility_index) # 0.42
47
+ print(risk.updated_at) # 2026-09-17 15:46:53
48
+ ```
49
+
50
+ ## Campos retornados (`PortRisk`)
51
+
52
+ | Campo | Tipo | Descrição |
53
+ |---|---|---|
54
+ | `port_id` | `str` | UN/LOCODE do porto (ex: `BRSSZ`) |
55
+ | `port_name` | `str` | Nome do porto |
56
+ | `country` | `str` | País do porto |
57
+ | `congestion_score` | `float` | Score de congestão (0.0 a 1.0) |
58
+ | `eta_delay_days` | `float` | Atraso estimado de ETA em dias |
59
+ | `waiting_vessels` | `int` | Navios aguardando/ancorados |
60
+ | `freight_volatility_index` | `float` | Índice de volatilidade de frete |
61
+ | `updated_at` | `str` | Timestamp da última atualização |
62
+
63
+ ## Configuração avançada
64
+
65
+ ```python
66
+ client = OracleClient(
67
+ api_key="SUA_RAPIDAPI_KEY",
68
+ host="aether-x-port-congestion-oracle.p.rapidapi.com", # default
69
+ timeout=30.0, # segundos
70
+ )
71
+ ```
72
+
73
+ ## Portos suportados
74
+
75
+ Portos com dados de exemplo: `BRSSZ`, `BRRIO`, `CNSHA`, `CNNGB`, `SGSIN`, `NLRTM`, `USLAX`, `USNYC`, `DEHAM`, `MPTNG`, `AEDXB`, `KRPUS`, `GBLGP`, `ZACPT`, `MXZLO`.
76
+
77
+ Portos não cadastrados retornam uma estimativa global (`country="Global"`).
78
+
79
+ ## Uso assíncrono (async)
80
+
81
+ Para consultar múltiplos portos em paralelo (ideal para bots e fundos quantitativos). Requer o extra `async`:
82
+
83
+ ```bash
84
+ pip install "aetherx-oracle[async]"
85
+ ```
86
+
87
+ ```python
88
+ import asyncio
89
+ from aetherx import OracleClient
90
+
91
+ async def main():
92
+ client = OracleClient(api_key="SUA_RAPIDAPI_KEY")
93
+
94
+ # Um porto
95
+ risk = await client.get_port_risk_async("BRSSZ")
96
+ print(risk.congestion_score)
97
+
98
+ # Vários portos em paralelo
99
+ risks = await client.get_ports_risk_async(["BRSSZ", "CNSHA", "NLRTM"])
100
+ for r in risks:
101
+ print(r.port_id, r.congestion_score)
102
+
103
+ asyncio.run(main())
104
+ ```
105
+
106
+ ## Termos de uso
107
+
108
+ Os sinais são fornecidos "AS IS", sem garantia e **não constituem aconselhamento de investimento**. Consulte os [Termos de Serviço](https://aether-x-oracle-production.up.railway.app/terms).
@@ -0,0 +1,87 @@
1
+ # aetherx-oracle
2
+
3
+ SDK Python oficial para a **Aether-X Port Congestion Oracle API** — sinais preditivos de congestão portuária, atraso de ETA e volatilidade de frete para portos globais.
4
+
5
+ ## Instalação
6
+
7
+ ```bash
8
+ pip install aetherx-oracle
9
+ ```
10
+
11
+ ## Uso rápido
12
+
13
+ ```python
14
+ from aetherx import OracleClient
15
+
16
+ client = OracleClient(api_key="SUA_RAPIDAPI_KEY")
17
+
18
+ risk = client.get_port_risk("BRSSZ")
19
+
20
+ print(risk.port_name) # Santos
21
+ print(risk.country) # Brasil
22
+ print(risk.congestion_score) # 0.78
23
+ print(risk.eta_delay_days) # 1.6
24
+ print(risk.waiting_vessels) # 12
25
+ print(risk.freight_volatility_index) # 0.42
26
+ print(risk.updated_at) # 2026-09-17 15:46:53
27
+ ```
28
+
29
+ ## Campos retornados (`PortRisk`)
30
+
31
+ | Campo | Tipo | Descrição |
32
+ |---|---|---|
33
+ | `port_id` | `str` | UN/LOCODE do porto (ex: `BRSSZ`) |
34
+ | `port_name` | `str` | Nome do porto |
35
+ | `country` | `str` | País do porto |
36
+ | `congestion_score` | `float` | Score de congestão (0.0 a 1.0) |
37
+ | `eta_delay_days` | `float` | Atraso estimado de ETA em dias |
38
+ | `waiting_vessels` | `int` | Navios aguardando/ancorados |
39
+ | `freight_volatility_index` | `float` | Índice de volatilidade de frete |
40
+ | `updated_at` | `str` | Timestamp da última atualização |
41
+
42
+ ## Configuração avançada
43
+
44
+ ```python
45
+ client = OracleClient(
46
+ api_key="SUA_RAPIDAPI_KEY",
47
+ host="aether-x-port-congestion-oracle.p.rapidapi.com", # default
48
+ timeout=30.0, # segundos
49
+ )
50
+ ```
51
+
52
+ ## Portos suportados
53
+
54
+ Portos com dados de exemplo: `BRSSZ`, `BRRIO`, `CNSHA`, `CNNGB`, `SGSIN`, `NLRTM`, `USLAX`, `USNYC`, `DEHAM`, `MPTNG`, `AEDXB`, `KRPUS`, `GBLGP`, `ZACPT`, `MXZLO`.
55
+
56
+ Portos não cadastrados retornam uma estimativa global (`country="Global"`).
57
+
58
+ ## Uso assíncrono (async)
59
+
60
+ Para consultar múltiplos portos em paralelo (ideal para bots e fundos quantitativos). Requer o extra `async`:
61
+
62
+ ```bash
63
+ pip install "aetherx-oracle[async]"
64
+ ```
65
+
66
+ ```python
67
+ import asyncio
68
+ from aetherx import OracleClient
69
+
70
+ async def main():
71
+ client = OracleClient(api_key="SUA_RAPIDAPI_KEY")
72
+
73
+ # Um porto
74
+ risk = await client.get_port_risk_async("BRSSZ")
75
+ print(risk.congestion_score)
76
+
77
+ # Vários portos em paralelo
78
+ risks = await client.get_ports_risk_async(["BRSSZ", "CNSHA", "NLRTM"])
79
+ for r in risks:
80
+ print(r.port_id, r.congestion_score)
81
+
82
+ asyncio.run(main())
83
+ ```
84
+
85
+ ## Termos de uso
86
+
87
+ Os sinais são fornecidos "AS IS", sem garantia e **não constituem aconselhamento de investimento**. Consulte os [Termos de Serviço](https://aether-x-oracle-production.up.railway.app/terms).
@@ -0,0 +1,5 @@
1
+ from aetherx.client import OracleClient, PortRisk
2
+
3
+ __version__ = "0.3.0"
4
+
5
+ __all__ = ["OracleClient", "PortRisk", "__version__"]
@@ -0,0 +1,118 @@
1
+ """Aether-X Port Congestion Oracle - Python SDK client."""
2
+
3
+ import asyncio
4
+ from typing import List, Optional
5
+
6
+ import requests
7
+ from pydantic import BaseModel, Field
8
+
9
+ DEFAULT_RAPIDAPI_HOST = "aether-x-port-congestion-oracle.p.rapidapi.com"
10
+ RISK_ENDPOINT = "/v1/port-risk"
11
+
12
+
13
+ class PortRisk(BaseModel):
14
+ """Predictive congestion signal for a single port."""
15
+
16
+ port_id: str = Field(..., description="UN/LOCODE do porto (ex: BRSSZ)")
17
+ port_name: str = Field(..., description="Nome do porto")
18
+ country: str = Field(..., description="País do porto")
19
+ congestion_score: float = Field(..., description="Score de congestão (0.0 a 1.0)")
20
+ eta_delay_days: float = Field(..., description="Atraso estimado de ETA em dias")
21
+ waiting_vessels: int = Field(..., description="Navios aguardando/ancorados")
22
+ freight_volatility_index: float = Field(..., description="Índice de volatilidade de frete")
23
+ updated_at: str = Field(..., description="Timestamp da última atualização")
24
+
25
+
26
+ class OracleClient:
27
+ """Cliente para a API Aether-X Port Congestion Oracle (via RapidAPI).
28
+
29
+ Exemplo:
30
+ >>> from aetherx import OracleClient
31
+ >>> client = OracleClient(api_key="SUA_RAPIDAPI_KEY")
32
+ >>> risk = client.get_port_risk("BRSSZ")
33
+ >>> print(risk.congestion_score, risk.waiting_vessels)
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ api_key: str,
39
+ host: str = DEFAULT_RAPIDAPI_HOST,
40
+ base_url: Optional[str] = None,
41
+ timeout: float = 30.0,
42
+ ) -> None:
43
+ if not api_key:
44
+ raise ValueError("api_key é obrigatória (sua chave da RapidAPI).")
45
+
46
+ self.api_key = api_key
47
+ self.host = host
48
+ self.base_url = (base_url or f"https://{host}").rstrip("/")
49
+ self.timeout = timeout
50
+
51
+ def get_port_risk(self, port_id: str) -> PortRisk:
52
+ """Retorna o sinal preditivo de congestão para um porto.
53
+
54
+ Args:
55
+ port_id: Código UN/LOCODE do porto (ex: "BRSSZ", "CNSHA").
56
+
57
+ Returns:
58
+ PortRisk com acesso direto via atributo.
59
+
60
+ Raises:
61
+ ValueError: se port_id for vazio.
62
+ requests.HTTPError: se a API retornar um status de erro.
63
+ """
64
+ url, headers, params = self._build_request(port_id)
65
+ response = requests.get(
66
+ url, headers=headers, params=params, timeout=self.timeout
67
+ )
68
+ response.raise_for_status()
69
+ return PortRisk.model_validate(response.json())
70
+
71
+ async def get_port_risk_async(self, port_id: str) -> PortRisk:
72
+ """Versão assíncrona de :meth:`get_port_risk` (requer o extra ``async``).
73
+
74
+ Uso:
75
+ >>> import asyncio
76
+ >>> client = OracleClient(api_key="SUA_RAPIDAPI_KEY")
77
+ >>> risk = asyncio.run(client.get_port_risk_async("BRSSZ"))
78
+
79
+ Instale com: ``pip install aetherx-oracle[async]``
80
+ """
81
+ try:
82
+ import httpx
83
+ except ImportError as exc: # pragma: no cover - depende do ambiente
84
+ raise ImportError(
85
+ "httpx é necessário para métodos assíncronos. "
86
+ "Instale com: pip install aetherx-oracle[async]"
87
+ ) from exc
88
+
89
+ url, headers, params = self._build_request(port_id)
90
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
91
+ response = await client.get(url, headers=headers, params=params)
92
+ response.raise_for_status()
93
+ return PortRisk.model_validate(response.json())
94
+
95
+ async def get_ports_risk_async(self, port_ids: List[str]) -> List[PortRisk]:
96
+ """Consulta vários portos em paralelo via ``asyncio.gather``.
97
+
98
+ Ideal para fundos quantitativos e bots que monitoram uma carteira de portos.
99
+ """
100
+ if not port_ids:
101
+ return []
102
+ return list(
103
+ await asyncio.gather(
104
+ *(self.get_port_risk_async(port_id) for port_id in port_ids)
105
+ )
106
+ )
107
+
108
+ def _build_request(self, port_id: str):
109
+ if not port_id:
110
+ raise ValueError("port_id é obrigatório (ex: 'BRSSZ').")
111
+
112
+ url = f"{self.base_url}{RISK_ENDPOINT}"
113
+ headers = {
114
+ "x-rapidapi-key": self.api_key,
115
+ "x-rapidapi-host": self.host,
116
+ }
117
+ params = {"port_id": port_id}
118
+ return url, headers, params
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: aetherx-oracle
3
+ Version: 0.3.0
4
+ Summary: Python SDK for the Aether-X Port Congestion Oracle API
5
+ Author: Aether-X Oracle Engine
6
+ License: Proprietary - Machine-to-Machine Data Distribution
7
+ Project-URL: Homepage, https://aether-x-oracle-production.up.railway.app
8
+ Project-URL: Documentation, https://aether-x-oracle-production.up.railway.app/docs
9
+ Project-URL: Terms, https://aether-x-oracle-production.up.railway.app/terms
10
+ Keywords: port,congestion,oracle,logistics,shipping,quant,freight
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: requests>=2.25.0
14
+ Requires-Dist: pydantic>=2.0.0
15
+ Provides-Extra: async
16
+ Requires-Dist: httpx>=0.24.0; extra == "async"
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest>=7.0; extra == "test"
19
+ Requires-Dist: responses>=0.23; extra == "test"
20
+ Requires-Dist: httpx>=0.24.0; extra == "test"
21
+
22
+ # aetherx-oracle
23
+
24
+ SDK Python oficial para a **Aether-X Port Congestion Oracle API** — sinais preditivos de congestão portuária, atraso de ETA e volatilidade de frete para portos globais.
25
+
26
+ ## Instalação
27
+
28
+ ```bash
29
+ pip install aetherx-oracle
30
+ ```
31
+
32
+ ## Uso rápido
33
+
34
+ ```python
35
+ from aetherx import OracleClient
36
+
37
+ client = OracleClient(api_key="SUA_RAPIDAPI_KEY")
38
+
39
+ risk = client.get_port_risk("BRSSZ")
40
+
41
+ print(risk.port_name) # Santos
42
+ print(risk.country) # Brasil
43
+ print(risk.congestion_score) # 0.78
44
+ print(risk.eta_delay_days) # 1.6
45
+ print(risk.waiting_vessels) # 12
46
+ print(risk.freight_volatility_index) # 0.42
47
+ print(risk.updated_at) # 2026-09-17 15:46:53
48
+ ```
49
+
50
+ ## Campos retornados (`PortRisk`)
51
+
52
+ | Campo | Tipo | Descrição |
53
+ |---|---|---|
54
+ | `port_id` | `str` | UN/LOCODE do porto (ex: `BRSSZ`) |
55
+ | `port_name` | `str` | Nome do porto |
56
+ | `country` | `str` | País do porto |
57
+ | `congestion_score` | `float` | Score de congestão (0.0 a 1.0) |
58
+ | `eta_delay_days` | `float` | Atraso estimado de ETA em dias |
59
+ | `waiting_vessels` | `int` | Navios aguardando/ancorados |
60
+ | `freight_volatility_index` | `float` | Índice de volatilidade de frete |
61
+ | `updated_at` | `str` | Timestamp da última atualização |
62
+
63
+ ## Configuração avançada
64
+
65
+ ```python
66
+ client = OracleClient(
67
+ api_key="SUA_RAPIDAPI_KEY",
68
+ host="aether-x-port-congestion-oracle.p.rapidapi.com", # default
69
+ timeout=30.0, # segundos
70
+ )
71
+ ```
72
+
73
+ ## Portos suportados
74
+
75
+ Portos com dados de exemplo: `BRSSZ`, `BRRIO`, `CNSHA`, `CNNGB`, `SGSIN`, `NLRTM`, `USLAX`, `USNYC`, `DEHAM`, `MPTNG`, `AEDXB`, `KRPUS`, `GBLGP`, `ZACPT`, `MXZLO`.
76
+
77
+ Portos não cadastrados retornam uma estimativa global (`country="Global"`).
78
+
79
+ ## Uso assíncrono (async)
80
+
81
+ Para consultar múltiplos portos em paralelo (ideal para bots e fundos quantitativos). Requer o extra `async`:
82
+
83
+ ```bash
84
+ pip install "aetherx-oracle[async]"
85
+ ```
86
+
87
+ ```python
88
+ import asyncio
89
+ from aetherx import OracleClient
90
+
91
+ async def main():
92
+ client = OracleClient(api_key="SUA_RAPIDAPI_KEY")
93
+
94
+ # Um porto
95
+ risk = await client.get_port_risk_async("BRSSZ")
96
+ print(risk.congestion_score)
97
+
98
+ # Vários portos em paralelo
99
+ risks = await client.get_ports_risk_async(["BRSSZ", "CNSHA", "NLRTM"])
100
+ for r in risks:
101
+ print(r.port_id, r.congestion_score)
102
+
103
+ asyncio.run(main())
104
+ ```
105
+
106
+ ## Termos de uso
107
+
108
+ Os sinais são fornecidos "AS IS", sem garantia e **não constituem aconselhamento de investimento**. Consulte os [Termos de Serviço](https://aether-x-oracle-production.up.railway.app/terms).
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ aetherx/__init__.py
4
+ aetherx/client.py
5
+ aetherx_oracle.egg-info/PKG-INFO
6
+ aetherx_oracle.egg-info/SOURCES.txt
7
+ aetherx_oracle.egg-info/dependency_links.txt
8
+ aetherx_oracle.egg-info/requires.txt
9
+ aetherx_oracle.egg-info/top_level.txt
10
+ tests/test_sdk.py
@@ -0,0 +1,10 @@
1
+ requests>=2.25.0
2
+ pydantic>=2.0.0
3
+
4
+ [async]
5
+ httpx>=0.24.0
6
+
7
+ [test]
8
+ pytest>=7.0
9
+ responses>=0.23
10
+ httpx>=0.24.0
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "aetherx-oracle"
7
+ version = "0.3.0"
8
+ description = "Python SDK for the Aether-X Port Congestion Oracle API"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "Proprietary - Machine-to-Machine Data Distribution" }
12
+ authors = [{ name = "Aether-X Oracle Engine" }]
13
+ keywords = ["port", "congestion", "oracle", "logistics", "shipping", "quant", "freight"]
14
+ dependencies = [
15
+ "requests>=2.25.0",
16
+ "pydantic>=2.0.0",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ async = ["httpx>=0.24.0"]
21
+ test = ["pytest>=7.0", "responses>=0.23", "httpx>=0.24.0"]
22
+
23
+ [project.urls]
24
+ Homepage = "https://aether-x-oracle-production.up.railway.app"
25
+ Documentation = "https://aether-x-oracle-production.up.railway.app/docs"
26
+ Terms = "https://aether-x-oracle-production.up.railway.app/terms"
27
+
28
+ [tool.setuptools.packages.find]
29
+ where = ["."]
30
+ include = ["aetherx*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,144 @@
1
+ import asyncio
2
+ import os
3
+ import sys
4
+
5
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
6
+
7
+ import httpx
8
+ import pytest
9
+ import requests
10
+ import responses
11
+
12
+ from aetherx import OracleClient, PortRisk
13
+
14
+ HOST = "aether-x-port-congestion-oracle.p.rapidapi.com"
15
+ URL = f"https://{HOST}/v1/port-risk"
16
+
17
+ SAMPLE = {
18
+ "port_id": "BRSSZ",
19
+ "port_name": "Santos",
20
+ "country": "Brasil",
21
+ "congestion_score": 0.78,
22
+ "eta_delay_days": 1.6,
23
+ "waiting_vessels": 12,
24
+ "freight_volatility_index": 0.42,
25
+ "updated_at": "2026-09-17 15:46:53",
26
+ }
27
+
28
+ FALLBACK = {
29
+ "port_id": "ZZZ99",
30
+ "port_name": "Unknown Port (Global Estimate)",
31
+ "country": "Global",
32
+ "congestion_score": 0.45,
33
+ "eta_delay_days": 1.0,
34
+ "waiting_vessels": 6,
35
+ "freight_volatility_index": 0.35,
36
+ "updated_at": "2026-09-17 15:47:19",
37
+ }
38
+
39
+
40
+ def make_client() -> OracleClient:
41
+ return OracleClient(api_key="test-key")
42
+
43
+
44
+ def test_empty_api_key_raises():
45
+ with pytest.raises(ValueError):
46
+ OracleClient(api_key="")
47
+
48
+
49
+ def test_empty_port_id_raises():
50
+ with pytest.raises(ValueError):
51
+ make_client().get_port_risk("")
52
+
53
+
54
+ @responses.activate
55
+ def test_get_port_risk_parses_response():
56
+ responses.add(responses.GET, URL, json=SAMPLE, status=200)
57
+
58
+ risk = make_client().get_port_risk("BRSSZ")
59
+
60
+ assert isinstance(risk, PortRisk)
61
+ assert risk.port_id == "BRSSZ"
62
+ assert risk.port_name == "Santos"
63
+ assert risk.country == "Brasil"
64
+ assert risk.congestion_score == 0.78
65
+ assert risk.eta_delay_days == 1.6
66
+ assert risk.waiting_vessels == 12
67
+ assert risk.freight_volatility_index == 0.42
68
+ assert risk.updated_at == "2026-09-17 15:46:53"
69
+
70
+
71
+ @responses.activate
72
+ def test_sends_rapidapi_headers_and_query_param():
73
+ responses.add(responses.GET, URL, json=SAMPLE, status=200)
74
+
75
+ make_client().get_port_risk("BRSSZ")
76
+
77
+ request = responses.calls[0].request
78
+ assert request.headers["x-rapidapi-key"] == "test-key"
79
+ assert request.headers["x-rapidapi-host"] == HOST
80
+ assert "port_id=BRSSZ" in request.url
81
+
82
+
83
+ @responses.activate
84
+ def test_fallback_payload_parses():
85
+ responses.add(responses.GET, URL, json=FALLBACK, status=200)
86
+
87
+ risk = make_client().get_port_risk("ZZZ99")
88
+
89
+ assert risk.country == "Global"
90
+ assert risk.congestion_score == 0.45
91
+ assert risk.waiting_vessels == 6
92
+
93
+
94
+ @responses.activate
95
+ def test_http_error_is_raised():
96
+ responses.add(responses.GET, URL, json={"detail": "boom"}, status=500)
97
+
98
+ with pytest.raises(requests.HTTPError):
99
+ make_client().get_port_risk("BRSSZ")
100
+
101
+
102
+ def _patch_httpx(monkeypatch, handler):
103
+ real_async_client = httpx.AsyncClient
104
+ transport = httpx.MockTransport(handler)
105
+
106
+ def factory(*args, **kwargs):
107
+ kwargs["transport"] = transport
108
+ return real_async_client(*args, **kwargs)
109
+
110
+ monkeypatch.setattr(httpx, "AsyncClient", factory)
111
+
112
+
113
+ def test_get_port_risk_async_parses(monkeypatch):
114
+ def handler(request):
115
+ assert request.headers["x-rapidapi-key"] == "test-key"
116
+ assert request.headers["x-rapidapi-host"] == HOST
117
+ return httpx.Response(200, json=SAMPLE)
118
+
119
+ _patch_httpx(monkeypatch, handler)
120
+
121
+ risk = asyncio.run(make_client().get_port_risk_async("BRSSZ"))
122
+
123
+ assert risk.port_name == "Santos"
124
+ assert risk.waiting_vessels == 12
125
+
126
+
127
+ def test_get_ports_risk_async_gathers_multiple(monkeypatch):
128
+ def handler(request):
129
+ port_id = dict(request.url.params)["port_id"]
130
+ payload = {**SAMPLE, "port_id": port_id, "port_name": f"Port {port_id}"}
131
+ return httpx.Response(200, json=payload)
132
+
133
+ _patch_httpx(monkeypatch, handler)
134
+
135
+ results = asyncio.run(
136
+ make_client().get_ports_risk_async(["BRSSZ", "CNSHA", "NLRTM"])
137
+ )
138
+
139
+ assert len(results) == 3
140
+ assert [r.port_id for r in results] == ["BRSSZ", "CNSHA", "NLRTM"]
141
+
142
+
143
+ def test_get_ports_risk_async_empty_list():
144
+ assert asyncio.run(make_client().get_ports_risk_async([])) == []