From 09c208f151ed798d15796f483c380ebd7187aa72 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <84702959+andrii-balitskyi@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:33:54 +0200 Subject: [PATCH] fix: Handle correct action attempt error status (#125) * Fix poll_until_ready error case * Add test_wait_for_action_attempt_waits_for_pending_action_attempt * Add test_wait_for_action_attempt_returns_successful_action_attempt * Add test_wait_for_action_attempt_times_out * Add test_wait_for_action_attempt_rejects_when_action_attempt_fails * Add test_wait_for_action_attempt_times_out_if_waiting_for_polling_interval * Update fake-seam-connect to 1.71.0 * Fix timeout duration --- package-lock.json | 1 + seam/modules/action_attempts.py | 2 +- test/wait_for_action_attempt_test.py | 160 ++++++++++++++++++++++++++- 3 files changed, 161 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8904f1d..3d07e20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -438,6 +438,7 @@ "resolved": "https://registry.npmjs.org/@seamapi/fake-seam-connect/-/fake-seam-connect-1.71.0.tgz", "integrity": "sha512-yhGB/kgOEj/nQv8GMMS27JezVwuQgr7bH1bugKxVVcvrKJVnJpebDUZCT1TeBUoc6P4rtE/BcL6TJPxrQmfRNw==", "dev": true, + "license": "MIT", "bin": { "fake-seam-connect": "dist/server.js" }, diff --git a/seam/modules/action_attempts.py b/seam/modules/action_attempts.py index 1f52630..d178baa 100644 --- a/seam/modules/action_attempts.py +++ b/seam/modules/action_attempts.py @@ -37,7 +37,7 @@ def poll_until_ready( action_attempt = get_action_attempt(client, action_attempt_id) - if action_attempt.status == "failed": + if action_attempt.status == "error": raise SeamActionAttemptFailedError(action_attempt) return action_attempt diff --git a/test/wait_for_action_attempt_test.py b/test/wait_for_action_attempt_test.py index 9b9118f..369178a 100644 --- a/test/wait_for_action_attempt_test.py +++ b/test/wait_for_action_attempt_test.py @@ -1,4 +1,6 @@ import pytest +from threading import Timer +from seam.exceptions import SeamActionAttemptTimeoutError, SeamActionAttemptFailedError from seam import Seam @@ -15,7 +17,7 @@ def test_wait_for_action_attempt_directly_on_returned_action_attempt(server): assert action_attempt.status == "success" -def test_wait_for_action_attempt_by_default(server): +def test_wait_for_action_attempt_waits_by_default(server): endpoint, seed = server seam = Seam.from_api_key(seed["seam_apikey1_token"], endpoint=endpoint) @@ -46,3 +48,159 @@ def test_wait_for_action_attempt_can_set_class_default_with_object(server): action_attempt = seam.locks.unlock_door(device_id=seed["august_device_1"]) assert action_attempt.status == "success" + + +def test_wait_for_action_attempt_waits_for_pending_action_attempt(server): + endpoint, seed = server + seam = Seam.from_api_key( + seed["seam_apikey1_token"], endpoint=endpoint, wait_for_action_attempt=False + ) + + action_attempt = seam.locks.unlock_door(device_id=seed["august_device_1"]) + + assert action_attempt.status == "pending" + + seam.client.post( + "/_fake/update_action_attempt", + json={ + "action_attempt_id": action_attempt.action_attempt_id, + "status": "pending", + }, + ) + + def update_action_attempt(): + seam.client.post( + "/_fake/update_action_attempt", + json={ + "action_attempt_id": action_attempt.action_attempt_id, + "status": "success", + }, + ) + + # Use Timer to schedule the update after 1 second + t = Timer(1.0, update_action_attempt) + t.start() + + resolved_action_attempt = seam.action_attempts.get( + action_attempt_id=action_attempt.action_attempt_id, wait_for_action_attempt=True + ) + + assert resolved_action_attempt.status == "success" + + +def test_wait_for_action_attempt_returns_successful_action_attempt(server): + endpoint, seed = server + seam = Seam.from_api_key( + seed["seam_apikey1_token"], endpoint=endpoint, wait_for_action_attempt=False + ) + + action_attempt = seam.locks.unlock_door(device_id=seed["august_device_1"]) + + assert action_attempt.status == "pending" + + seam.client.post( + "/_fake/update_action_attempt", + json={ + "action_attempt_id": action_attempt.action_attempt_id, + "status": "success", + }, + ) + + successful_action_attempt = seam.action_attempts.get( + action_attempt_id=action_attempt.action_attempt_id + ) + + assert successful_action_attempt.status == "success" + + resolved_action_attempt = seam.action_attempts.get( + action_attempt_id=action_attempt.action_attempt_id, wait_for_action_attempt=True + ) + + assert resolved_action_attempt == successful_action_attempt + + +def test_wait_for_action_attempt_times_out(server): + endpoint, seed = server + seam = Seam.from_api_key( + seed["seam_apikey1_token"], endpoint=endpoint, wait_for_action_attempt=False + ) + + action_attempt = seam.locks.unlock_door(device_id=seed["august_device_1"]) + + assert action_attempt.status == "pending" + + seam.client.post( + "/_fake/update_action_attempt", + json={ + "action_attempt_id": action_attempt.action_attempt_id, + "status": "pending", + }, + ) + + with pytest.raises(SeamActionAttemptTimeoutError) as exc_info: + seam.action_attempts.get( + action_attempt_id=action_attempt.action_attempt_id, + wait_for_action_attempt={"timeout": 0.1}, + ) + + assert exc_info.value.action_attempt == action_attempt + + +def test_wait_for_action_attempt_rejects_when_action_attempt_fails(server): + endpoint, seed = server + seam = Seam.from_api_key( + seed["seam_apikey1_token"], endpoint=endpoint, wait_for_action_attempt=False + ) + + action_attempt = seam.locks.unlock_door(device_id=seed["august_device_1"]) + + assert action_attempt.status == "pending" + + seam.client.post( + "/_fake/update_action_attempt", + json={ + "action_attempt_id": action_attempt.action_attempt_id, + "status": "error", + "error": {"message": "Failed", "type": "foo"}, + }, + ) + + with pytest.raises(SeamActionAttemptFailedError, match="Failed") as exc_info: + seam.action_attempts.get( + action_attempt_id=action_attempt.action_attempt_id, + wait_for_action_attempt=True, + ) + + assert ( + exc_info.value.action_attempt.action_attempt_id + == action_attempt.action_attempt_id + ) + assert exc_info.value.action_attempt.status == "error" + assert exc_info.value.code == "foo" + + +def test_wait_for_action_attempt_times_out_if_waiting_for_polling_interval(server): + endpoint, seed = server + seam = Seam.from_api_key( + seed["seam_apikey1_token"], endpoint=endpoint, wait_for_action_attempt=False + ) + + action_attempt = seam.locks.unlock_door(device_id=seed["august_device_1"]) + + assert action_attempt.status == "pending" + + seam.client.post( + "/_fake/update_action_attempt", + json={ + "action_attempt_id": action_attempt.action_attempt_id, + "status": "pending", + }, + ) + + with pytest.raises(SeamActionAttemptTimeoutError) as exc_info: + seam.action_attempts.get( + action_attempt_id=action_attempt.action_attempt_id, + wait_for_action_attempt={"timeout": 0.5, "polling_interval": 5}, + ) + + assert exc_info.value.action_attempt == action_attempt