Skip to content

Commit a614d19

Browse files
authored
Merge pull request #385 from planetlabs/v2-cli-tests-327
Convert CLI tests to integration tests, increase overall test coverage, slight change to API url building logistics
2 parents 53012b0 + 87a0f0d commit a614d19

File tree

15 files changed

+784
-568
lines changed

15 files changed

+784
-568
lines changed

planet/auth.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@
2121
import httpx
2222
import jwt
2323

24-
from . import constants, http, models
24+
from . import http, models
25+
from .constants import PLANET_BASE_URL, SECRET_FILE_PATH
2526
from .exceptions import AuthException
2627

27-
2828
LOGGER = logging.getLogger(__name__)
2929

30-
BASE_URL = constants.PLANET_BASE_URL + 'v0/auth/'
30+
BASE_URL = f'{PLANET_BASE_URL}/v0/auth'
3131
ENV_API_KEY = 'PL_API_KEY'
32-
SECRET_FILE_PATH = os.path.join(os.path.expanduser('~'), '.planet.json')
3332

3433

3534
class Auth(metaclass=abc.ABCMeta):
@@ -155,11 +154,12 @@ def __init__(
155154
):
156155
"""
157156
Parameters:
158-
base_url: Alternate authentication api base URL.
157+
base_url: The base URL to use. Defaults to production
158+
authentication API base url.
159159
"""
160160
self._base_url = base_url or BASE_URL
161-
if not self._base_url.endswith('/'):
162-
self._base_url += '/'
161+
if self._base_url.endswith('/'):
162+
self._base_url = self._base_url[:-1]
163163

164164
def login(
165165
self,
@@ -179,7 +179,7 @@ def login(
179179
A JSON object containing an `api_key` property with the user's
180180
API_KEY.
181181
'''
182-
url = self._base_url + 'login'
182+
url = f'{self._base_url}/login'
183183
data = {'email': email,
184184
'password': password
185185
}

planet/cli/auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def auth(ctx, base_url):
4343
))
4444
def init(ctx, email, password):
4545
'''Obtain and store authentication information'''
46-
base_url = ctx.obj["BASE_URL"]
46+
base_url = ctx.obj['BASE_URL']
4747
plauth = planet.Auth.from_login(email, password, base_url=base_url)
4848
plauth.write()
4949
click.echo('Initialized')

planet/cli/orders.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ async def orders_client(ctx):
4646
help='Assign custom base Orders API URL.')
4747
def orders(ctx, base_url):
4848
'''Commands for interacting with the Orders API'''
49-
auth = planet.Auth.from_file()
50-
ctx.obj['AUTH'] = auth
49+
ctx.obj['AUTH'] = planet.Auth.from_file()
5150
ctx.obj['BASE_URL'] = base_url
5251

5352

@@ -157,8 +156,6 @@ def read_file_json(ctx, param, value):
157156
json_value = json.load(value)
158157
except json.decoder.JSONDecodeError:
159158
raise click.ClickException('File does not contain valid json.')
160-
except click.FileError as e:
161-
raise click.ClickException(e)
162159

163160
return json_value
164161

planet/clients/orders.py

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@
2020
import typing
2121
import uuid
2222

23-
from .. import constants, exceptions
23+
from .. import exceptions
24+
from ..constants import PLANET_BASE_URL
2425
from ..http import Session
2526
from ..models import Order, Orders, Request, Response, StreamingBody
2627

2728

28-
BASE_URL = constants.PLANET_BASE_URL + 'compute/ops/'
29-
STATS_PATH = 'stats/orders/v2/'
30-
ORDERS_PATH = 'orders/v2/'
31-
BULK_PATH = 'bulk/orders/v2/'
29+
BASE_URL = f'{PLANET_BASE_URL}compute/ops'
30+
STATS_PATH = '/stats/orders/v2'
31+
ORDERS_PATH = '/orders/v2'
32+
BULK_PATH = '/bulk/orders/v2'
3233

3334
# Order states https://developers.planet.com/docs/orders/ordering/#order-states
3435
ORDERS_STATES_COMPLETE = ['success', 'partial', 'cancelled', 'failed']
@@ -52,16 +53,13 @@ class OrdersClient():
5253
>>> from planet import Session, OrdersClient
5354
>>>
5455
>>> async def main():
55-
... auth = ('example_api_key', '')
56-
... async with Session(auth=auth) as sess:
56+
... async with Session() as sess:
5757
... cl = OrdersClient(sess)
5858
... # use client here
5959
...
6060
>>> asyncio.run(main())
6161
6262
```
63-
64-
6563
"""
6664
def __init__(
6765
self,
@@ -77,8 +75,8 @@ def __init__(
7775
self._session = session
7876

7977
self._base_url = base_url or BASE_URL
80-
if not self._base_url.endswith('/'):
81-
self._base_url += '/'
78+
if self._base_url.endswith('/'):
79+
self._base_url = self._base_url[:-1]
8280

8381
@staticmethod
8482
def _check_order_id(oid):
@@ -92,17 +90,10 @@ def _check_order_id(oid):
9290
raise OrdersClientException(msg)
9391

9492
def _orders_url(self):
95-
return self._base_url + ORDERS_PATH
93+
return f'{self._base_url}{ORDERS_PATH}'
9694

9795
def _stats_url(self):
98-
return self._base_url + STATS_PATH
99-
100-
def _order_url(self, order_id):
101-
self._check_order_id(order_id)
102-
return self._orders_url() + order_id
103-
104-
def _bulk_url(self):
105-
return self._base_url + BULK_PATH
96+
return f'{self._base_url}{STATS_PATH}'
10697

10798
def _request(self, url, method, data=None, params=None, json=None):
10899
return Request(url, method=method, data=data, params=params, json=json)
@@ -190,7 +181,8 @@ async def get_order(
190181
OrdersClientException: If order_id is not valid UUID.
191182
planet.exceptions.APIException: On API error.
192183
'''
193-
url = self._order_url(order_id)
184+
self._check_order_id(order_id)
185+
url = f'{self._orders_url()}/{order_id}'
194186

195187
req = self._request(url, method='GET')
196188

@@ -224,7 +216,9 @@ async def cancel_order(
224216
OrdersClientException: If order_id is not valid UUID.
225217
planet.exceptions.APIException: On API error.
226218
'''
227-
url = self._order_url(order_id)
219+
self._check_order_id(order_id)
220+
url = f'{self._orders_url()}/{order_id}'
221+
228222
req = self._request(url, method='PUT')
229223

230224
try:
@@ -252,7 +246,7 @@ async def cancel_orders(
252246
Raises:
253247
planet.exceptions.APIException: On API error.
254248
'''
255-
url = self._bulk_url() + 'cancel'
249+
url = f'{self._base_url}{BULK_PATH}/cancel'
256250
cancel_body = {}
257251
if order_ids:
258252
for oid in order_ids:

planet/constants.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
'''Constants used across the code base'''
15+
import os
1516

16-
PLANET_BASE_URL = 'https://api.planet.com/'
17+
PLANET_BASE_URL = 'https://api.planet.com'
18+
19+
SECRET_FILE_PATH = os.path.join(os.path.expanduser('~'), '.planet.json')

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ addopts =
1010
--cov=tests
1111
--cov-report=term-missing
1212
--cov-report=xml
13-
--cov-fail-under 95
13+
--cov-fail-under 97
1414
-rxXs
1515

1616
[coverage:run]

tests/conftest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from planet.auth import _SecretFile
2121

22+
2223
_here = Path(os.path.abspath(os.path.dirname(__file__)))
2324
_test_data_path = _here / 'data'
2425

@@ -29,6 +30,8 @@ def test_secretfile_read():
2930
def mockreturn(self):
3031
return {'key': 'testkey'}
3132

33+
# monkeypatch fixture is not available above a function scope
34+
# usage: https://docs.pytest.org/en/6.2.x/reference.html#pytest.MonkeyPatch
3235
with pytest.MonkeyPatch.context() as mp:
3336
mp.setattr(_SecretFile, 'read', mockreturn)
3437
yield

tests/integration/conftest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import contextlib
15+
import copy
1516

1617
import pytest
1718

@@ -33,3 +34,14 @@ def cm(ex, msg):
3334
with pytest.raises(ex, match=f'^{msg}$') as pt:
3435
yield pt
3536
return cm
37+
38+
39+
@pytest.fixture
40+
def order_descriptions(order_description):
41+
order1 = order_description
42+
order1['id'] = 'oid1'
43+
order2 = copy.deepcopy(order_description)
44+
order2['id'] = 'oid2'
45+
order3 = copy.deepcopy(order_description)
46+
order3['id'] = 'oid3'
47+
return [order1, order2, order3]

tests/integration/test_auth_api.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,18 @@
2323
from planet.auth import AuthClient
2424

2525

26-
TEST_URL = 'http://MockNotRealURL/'
26+
TEST_URL = 'http://MockNotRealURL/api/path'
27+
TEST_LOGIN_URL = f'{TEST_URL}/login'
2728

2829
LOGGER = logging.getLogger(__name__)
2930

3031

3132
@respx.mock
3233
def test_AuthClient_success():
33-
login_url = TEST_URL + 'login'
34-
3534
payload = {'api_key': 'iamakey'}
3635
resp = {'token': jwt.encode(payload, 'key')}
3736
mock_resp = httpx.Response(HTTPStatus.OK, json=resp)
38-
respx.post(login_url).return_value = mock_resp
37+
respx.post(TEST_LOGIN_URL).return_value = mock_resp
3938

4039
cl = AuthClient(base_url=TEST_URL)
4140
auth_data = cl.login('email', 'password')
@@ -45,8 +44,6 @@ def test_AuthClient_success():
4544

4645
@respx.mock
4746
def test_AuthClient_invalid_email():
48-
login_url = TEST_URL + 'login'
49-
5047
resp = {
5148
"errors": {
5249
"email": [
@@ -58,7 +55,7 @@ def test_AuthClient_invalid_email():
5855
"success": False
5956
}
6057
mock_resp = httpx.Response(400, json=resp)
61-
respx.post(login_url).return_value = mock_resp
58+
respx.post(TEST_LOGIN_URL).return_value = mock_resp
6259

6360
cl = AuthClient(base_url=TEST_URL)
6461
with pytest.raises(exceptions.APIException,
@@ -68,16 +65,14 @@ def test_AuthClient_invalid_email():
6865

6966
@respx.mock
7067
def test_AuthClient_invalid_password():
71-
login_url = TEST_URL + 'login'
72-
7368
resp = {
7469
"errors": None,
7570
"message": "Invalid email or password",
7671
"status": 401,
7772
"success": False
7873
}
7974
mock_resp = httpx.Response(401, json=resp)
80-
respx.post(login_url).return_value = mock_resp
75+
respx.post(TEST_LOGIN_URL).return_value = mock_resp
8176

8277
cl = AuthClient(base_url=TEST_URL)
8378
with pytest.raises(exceptions.APIException,

0 commit comments

Comments
 (0)