Skip to content

Commit c3c497d

Browse files
committed
add tests
1 parent 78a1768 commit c3c497d

File tree

7 files changed

+312
-7
lines changed

7 files changed

+312
-7
lines changed

planet/cli/destinations.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ async def _patch_destination(ctx, destination_id, data, pretty):
3636
raise ClickException(f"Failed to patch destination: {e}")
3737

3838

39-
async def _list_destinations(ctx, archived, is_owner, can_write, pretty):
39+
async def _list_destinations(ctx, archived, is_owner, can_write, is_default, pretty):
4040
async with destinations_client(ctx) as cl:
4141
try:
4242
response = await cl.list_destinations(archived,
4343
is_owner,
44-
can_write)
44+
can_write,
45+
is_default)
4546
echo_json(response, pretty)
4647
except Exception as e:
4748
raise ClickException(f"Failed to list destinations: {e}")
@@ -109,7 +110,13 @@ async def _get_default_destination(ctx, pretty):
109110
default=None,
110111
help="""Set to true to include only destinations the user can modify,
111112
false to exclude them.""")
112-
async def list_destinations(ctx, archived, is_owner, can_write, pretty):
113+
@click.option(
114+
'--is-default',
115+
type=bool,
116+
default=None,
117+
help="""Set to true to include only the default destination,
118+
false to exclude it.""")
119+
async def list_destinations(ctx, archived, is_owner, can_write, is_default, pretty):
113120
"""
114121
List destinations with optional filters
115122
@@ -120,7 +127,7 @@ async def list_destinations(ctx, archived, is_owner, can_write, pretty):
120127
121128
planet destinations list --archived false --is-owner true --can-write true
122129
"""
123-
await _list_destinations(ctx, archived, is_owner, can_write, pretty)
130+
await _list_destinations(ctx, archived, is_owner, can_write, is_default, pretty)
124131

125132

126133
@command(destinations, name="get")

planet/clients/destinations.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,16 @@ def __init__(self,
6060
async def list_destinations(self,
6161
archived: Optional[bool] = None,
6262
is_owner: Optional[bool] = None,
63-
can_write: Optional[bool] = None) -> Dict:
63+
can_write: Optional[bool] = None,
64+
is_default: Optional[bool] = None) -> Dict:
6465
"""
6566
List all destinations. By default, all non-archived destinations in the requesting user's org are returned.
6667
6768
Args:
6869
archived (bool): If True, include archived destinations.
6970
is_owner (bool): If True, include only destinations owned by the requesting user.
7071
can_write (bool): If True, include only destinations the requesting user can modify.
72+
is_default (bool): If True, include only the default destination.
7173
7274
Returns:
7375
dict: A dictionary containing the list of destinations inside the 'destinations' key.
@@ -83,6 +85,8 @@ async def list_destinations(self,
8385
params["is_owner"] = is_owner
8486
if can_write is not None:
8587
params["can_write"] = can_write
88+
if is_default is not None:
89+
params["is_default"] = is_default
8690

8791
try:
8892
response = await self._session.request(method='GET',

planet/sync/destinations.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,16 @@ def __init__(self, session: Session, base_url: Optional[str] = None):
3434
def list_destinations(self,
3535
archived: Optional[bool] = None,
3636
is_owner: Optional[bool] = None,
37-
can_write: Optional[bool] = None) -> Dict:
37+
can_write: Optional[bool] = None,
38+
is_default: Optional[bool] = None) -> Dict:
3839
"""
3940
List all destinations. By default, all non-archived destinations in the requesting user's org are returned.
4041
4142
Args:
4243
archived (bool): If True, include archived destinations.
4344
is_owner (bool): If True, include only destinations owned by the requesting user.
4445
can_write (bool): If True, include only destinations the requesting user can modify.
46+
is_default (bool): If True, include only the default destination.
4547
4648
Returns:
4749
dict: A dictionary containing the list of destinations inside the 'destinations' key.
@@ -51,7 +53,7 @@ def list_destinations(self,
5153
ClientError: If there is an issue with the client request.
5254
"""
5355
return self._client._call_sync(
54-
self._client.list_destinations(archived, is_owner, can_write))
56+
self._client.list_destinations(archived, is_owner, can_write, is_default))
5557

5658
def get_destination(self, destination_id: str) -> Dict:
5759
"""
@@ -105,3 +107,52 @@ def create_destination(self, request: Dict[str, Any]) -> Dict:
105107
"""
106108
return self._client._call_sync(
107109
self._client.create_destination(request))
110+
111+
def set_default_destination(self, destination_id: str) -> Dict:
112+
"""
113+
Set an existing destination as the default destination. Default destinations are globally available
114+
to all members of an organization. An organization can have zero or one default destination at any time.
115+
Ability to set a default destination is restricted to organization administrators and destination owners.
116+
117+
Args:
118+
destination_id (str): The ID of the destination to set as default.
119+
120+
Returns:
121+
dict: A dictionary containing the default destination details.
122+
123+
Raises:
124+
APIError: If the API returns an error response.
125+
ClientError: If there is an issue with the client request.
126+
"""
127+
return self._client._call_sync(
128+
self._client.set_default_destination(destination_id))
129+
130+
def unset_default_destination(self) -> None:
131+
"""
132+
Unset the current default destination. Ability to unset a default destination is restricted to
133+
organization administrators and destination owners. Returns None (HTTP 204, No Content) on success.
134+
135+
Returns:
136+
None
137+
138+
Raises:
139+
APIError: If the API returns an error response.
140+
ClientError: If there is an issue with the client request.
141+
"""
142+
return self._client._call_sync(
143+
self._client.unset_default_destination())
144+
145+
def get_default_destination(self) -> Dict:
146+
"""
147+
Get the current default destination. The default destination is globally available to all members of an
148+
organization.
149+
150+
Returns:
151+
dict: A dictionary containing the default destination details.
152+
153+
Raises:
154+
APIError: If the API returns an error response.
155+
ClientError: If there is an issue with the client request.
156+
"""
157+
return self._client._call_sync(
158+
self._client.get_default_destination())

tests/integration/test_destinations_api.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,83 @@ async def test_get_destination_not_found():
179179
await cl_async.get_destination(id)
180180
with pytest.raises(Exception):
181181
cl_sync.get_destination(id)
182+
183+
184+
@respx.mock
185+
async def test_set_default_destination():
186+
id = DEST_1["id"]
187+
url = f"{TEST_URL}/default"
188+
mock_response(url, DEST_1, method="put")
189+
190+
def assertf(resp):
191+
assert resp == DEST_1
192+
193+
assertf(await cl_async.set_default_destination(id))
194+
assertf(cl_sync.set_default_destination(id))
195+
196+
197+
@respx.mock
198+
async def test_set_default_destination_bad_request():
199+
id = "invalid_dest_id"
200+
url = f"{TEST_URL}/default"
201+
mock_response(url, {
202+
"code": 400, "message": "Bad Request: Invalid destination ID"
203+
},
204+
method="put",
205+
status_code=HTTPStatus.BAD_REQUEST)
206+
207+
with pytest.raises(Exception):
208+
await cl_async.set_default_destination(id)
209+
with pytest.raises(Exception):
210+
cl_sync.set_default_destination(id)
211+
212+
213+
@respx.mock
214+
async def test_get_default_destination():
215+
url = f"{TEST_URL}/default"
216+
mock_response(url, DEST_1)
217+
218+
def assertf(resp):
219+
assert resp == DEST_1
220+
221+
assertf(await cl_async.get_default_destination())
222+
assertf(cl_sync.get_default_destination())
223+
224+
225+
@respx.mock
226+
async def test_get_default_destination_not_found():
227+
url = f"{TEST_URL}/default"
228+
mock_response(url, {
229+
"code": 404, "message": "No default destination configured"
230+
},
231+
status_code=HTTPStatus.NOT_FOUND)
232+
233+
with pytest.raises(Exception):
234+
await cl_async.get_default_destination()
235+
with pytest.raises(Exception):
236+
cl_sync.get_default_destination()
237+
238+
239+
@respx.mock
240+
async def test_unset_default_destination():
241+
url = f"{TEST_URL}/default"
242+
mock_response(url, None, method="delete", status_code=HTTPStatus.NO_CONTENT)
243+
244+
# unset_default_destination returns None
245+
assert await cl_async.unset_default_destination() is None
246+
assert cl_sync.unset_default_destination() is None
247+
248+
249+
@respx.mock
250+
async def test_unset_default_destination_unauthorized():
251+
url = f"{TEST_URL}/default"
252+
mock_response(url, {
253+
"code": 401, "message": "Unauthorized: Insufficient permissions"
254+
},
255+
method="delete",
256+
status_code=HTTPStatus.UNAUTHORIZED)
257+
258+
with pytest.raises(Exception):
259+
await cl_async.unset_default_destination()
260+
with pytest.raises(Exception):
261+
cl_sync.unset_default_destination()

tests/integration/test_destinations_cli.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ def test_destinations_cli_list(invoke):
180180
result = invoke(['list', '--can-write', 'true'])
181181
assert result.exit_code == 0
182182

183+
result = invoke(['list', '--is-default', 'true'])
184+
assert result.exit_code == 0
185+
183186
result = invoke(['list', '--archived', 'false'])
184187
assert result.exit_code == 0
185188

@@ -189,6 +192,9 @@ def test_destinations_cli_list(invoke):
189192
result = invoke(['list', '--can-write', 'false'])
190193
assert result.exit_code == 0
191194

195+
result = invoke(['list', '--is-default', 'false'])
196+
assert result.exit_code == 0
197+
192198
result = invoke(['list', '--archived', 'false', '--is-owner', 'true'])
193199
assert result.exit_code == 0
194200

@@ -251,3 +257,66 @@ def test_destinations_cli_update(invoke):
251257
'--use-path-style'
252258
])
253259
assert result.exit_code == 0
260+
261+
262+
@respx.mock
263+
def test_destinations_cli_default_set(invoke):
264+
url = f"{TEST_DESTINATIONS_URL}/default"
265+
respx.put(url).return_value = httpx.Response(HTTPStatus.OK, json={})
266+
267+
result = invoke(['default', 'set', 'fake-dest-id'])
268+
assert result.exit_code == 0
269+
270+
271+
@respx.mock
272+
def test_destinations_cli_default_set_bad_request(invoke):
273+
url = f"{TEST_DESTINATIONS_URL}/default"
274+
respx.put(url).return_value = httpx.Response(
275+
HTTPStatus.BAD_REQUEST,
276+
json={"code": 400, "message": "Bad Request: Invalid destination ID"})
277+
278+
result = invoke(['default', 'set', 'invalid-dest-id'])
279+
assert result.exit_code != 0
280+
assert "Failed to set default destination" in result.output
281+
282+
283+
@respx.mock
284+
def test_destinations_cli_default_get(invoke):
285+
url = f"{TEST_DESTINATIONS_URL}/default"
286+
respx.get(url).return_value = httpx.Response(HTTPStatus.OK, json={})
287+
288+
result = invoke(['default', 'get'])
289+
assert result.exit_code == 0
290+
291+
292+
@respx.mock
293+
def test_destinations_cli_default_get_not_found(invoke):
294+
url = f"{TEST_DESTINATIONS_URL}/default"
295+
respx.get(url).return_value = httpx.Response(
296+
HTTPStatus.NOT_FOUND,
297+
json={"code": 404, "message": "No default destination configured"})
298+
299+
result = invoke(['default', 'get'])
300+
assert result.exit_code != 0
301+
assert "Failed to get default destination" in result.output
302+
303+
304+
@respx.mock
305+
def test_destinations_cli_default_unset(invoke):
306+
url = f"{TEST_DESTINATIONS_URL}/default"
307+
respx.delete(url).return_value = httpx.Response(HTTPStatus.NO_CONTENT)
308+
309+
result = invoke(['default', 'unset'])
310+
assert result.exit_code == 0
311+
312+
313+
@respx.mock
314+
def test_destinations_cli_default_unset_unauthorized(invoke):
315+
url = f"{TEST_DESTINATIONS_URL}/default"
316+
respx.delete(url).return_value = httpx.Response(
317+
HTTPStatus.UNAUTHORIZED,
318+
json={"code": 401, "message": "Unauthorized: Insufficient permissions"})
319+
320+
result = invoke(['default', 'unset'])
321+
assert result.exit_code != 0
322+
assert "Failed to unset default destination" in result.output

tests/unit/test_order_request.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,50 @@ def test_fallback_bundle_invalid(bundle, fallback_bundle):
469469
bundle,
470470
"PSScene",
471471
fallback_bundle=fallback_bundle)
472+
473+
474+
def test_destination():
475+
dest_config = order_request.destination('my-dest-ref')
476+
477+
expected = {
478+
'destination': {
479+
'ref': 'my-dest-ref'
480+
}
481+
}
482+
assert dest_config == expected
483+
484+
485+
def test_destination_path_prefix():
486+
dest_config = order_request.destination('my-dest-ref',
487+
path_prefix='my/prefix')
488+
489+
expected = {
490+
'destination': {
491+
'ref': 'my-dest-ref',
492+
'path_prefix': 'my/prefix'
493+
}
494+
}
495+
assert dest_config == expected
496+
497+
498+
def test_default_destination():
499+
dest_config = order_request.default_destination()
500+
501+
expected = {
502+
'destination': {
503+
'ref': 'pl:destinations/default'
504+
}
505+
}
506+
assert dest_config == expected
507+
508+
509+
def test_default_destination_path_prefix():
510+
dest_config = order_request.default_destination(path_prefix='my/prefix')
511+
512+
expected = {
513+
'destination': {
514+
'ref': 'pl:destinations/default',
515+
'path_prefix': 'my/prefix'
516+
}
517+
}
518+
assert dest_config == expected

0 commit comments

Comments
 (0)