futurehouse-client 0.3.20.dev208__tar.gz → 0.3.20.dev215__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.
Files changed (29) hide show
  1. {futurehouse_client-0.3.20.dev208/futurehouse_client.egg-info → futurehouse_client-0.3.20.dev215}/PKG-INFO +1 -1
  2. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/version.py +2 -2
  3. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215/futurehouse_client.egg-info}/PKG-INFO +1 -1
  4. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/tests/test_rest.py +128 -0
  5. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/LICENSE +0 -0
  6. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/README.md +0 -0
  7. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/docs/__init__.py +0 -0
  8. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/docs/client_notebook.ipynb +0 -0
  9. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/__init__.py +0 -0
  10. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/clients/__init__.py +0 -0
  11. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/clients/job_client.py +0 -0
  12. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/clients/rest_client.py +0 -0
  13. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/models/__init__.py +0 -0
  14. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/models/app.py +0 -0
  15. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/models/client.py +0 -0
  16. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/models/rest.py +0 -0
  17. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/py.typed +0 -0
  18. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/utils/__init__.py +0 -0
  19. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/utils/auth.py +0 -0
  20. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/utils/general.py +0 -0
  21. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/utils/module_utils.py +0 -0
  22. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client/utils/monitoring.py +0 -0
  23. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client.egg-info/SOURCES.txt +0 -0
  24. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client.egg-info/dependency_links.txt +0 -0
  25. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client.egg-info/requires.txt +0 -0
  26. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/futurehouse_client.egg-info/top_level.txt +0 -0
  27. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/pyproject.toml +0 -0
  28. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/setup.cfg +0 -0
  29. {futurehouse_client-0.3.20.dev208 → futurehouse_client-0.3.20.dev215}/tests/test_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: futurehouse-client
3
- Version: 0.3.20.dev208
3
+ Version: 0.3.20.dev215
4
4
  Summary: A client for interacting with endpoints of the FutureHouse service.
5
5
  Author-email: FutureHouse technical staff <hello@futurehouse.org>
6
6
  License: Apache License
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.3.20.dev208'
21
- __version_tuple__ = version_tuple = (0, 3, 20, 'dev208')
20
+ __version__ = version = '0.3.20.dev215'
21
+ __version_tuple__ = version_tuple = (0, 3, 20, 'dev215')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: futurehouse-client
3
- Version: 0.3.20.dev208
3
+ Version: 0.3.20.dev215
4
4
  Summary: A client for interacting with endpoints of the FutureHouse service.
5
5
  Author-email: FutureHouse technical staff <hello@futurehouse.org>
6
6
  License: Apache License
@@ -8,6 +8,7 @@ import time
8
8
  import types
9
9
  from pathlib import Path
10
10
  from unittest.mock import MagicMock, mock_open, patch
11
+ from uuid import UUID, uuid4
11
12
 
12
13
  import pytest
13
14
  from futurehouse_client.clients import (
@@ -15,6 +16,7 @@ from futurehouse_client.clients import (
15
16
  )
16
17
  from futurehouse_client.clients.rest_client import (
17
18
  FileUploadError,
19
+ ProjectError,
18
20
  RestClient,
19
21
  RestClientError,
20
22
  )
@@ -745,3 +747,129 @@ def test_world_model_create_and_get(admin_client: RestClient):
745
747
  assert updated_model_by_id.name == model.name
746
748
  assert updated_model_by_id.content != model.content
747
749
  assert updated_model_by_name.content != model.content
750
+
751
+
752
+ class TestProjectOperations:
753
+ @pytest.fixture
754
+ def test_project_name(self):
755
+ return f"test-project-{int(time.time())}"
756
+
757
+ def test_create_project_success(
758
+ self, admin_client: RestClient, test_project_name: str
759
+ ):
760
+ project_id = admin_client.create_project(test_project_name)
761
+ assert isinstance(project_id, str)
762
+ UUID(project_id)
763
+
764
+ def test_get_project_by_name_success(
765
+ self, admin_client: RestClient, test_project_name: str
766
+ ):
767
+ created_project_id = admin_client.create_project(test_project_name)
768
+ retrieved_project_id = admin_client.get_project_by_name(test_project_name)
769
+ assert str(retrieved_project_id) == created_project_id
770
+
771
+ def test_get_project_by_name_not_found(self, admin_client: RestClient):
772
+ with pytest.raises(ProjectError, match="No project found with name"):
773
+ admin_client.get_project_by_name("non-existent-project-12345")
774
+
775
+ def test_get_project_by_name_multiple_found(self, admin_client: RestClient):
776
+ with patch.object(admin_client.client, "get") as mock_get:
777
+ mock_response = MagicMock()
778
+ mock_response.raise_for_status.return_value = None
779
+ mock_response.json.return_value = [
780
+ {"id": "uuid1", "name": "test"},
781
+ {"id": "uuid2", "name": "test"},
782
+ ]
783
+ mock_get.return_value = mock_response
784
+
785
+ with pytest.raises(ProjectError, match="Multiple projects found"):
786
+ admin_client.get_project_by_name("test")
787
+
788
+ def test_add_task_to_project_success(
789
+ self, admin_client: RestClient, test_project_name: str, task_req: TaskRequest
790
+ ):
791
+ project_id = admin_client.create_project(test_project_name)
792
+ trajectory_id = admin_client.create_task(task_req)
793
+ admin_client.add_task_to_project(UUID(project_id), trajectory_id)
794
+
795
+ def test_add_task_to_project_not_found(self, admin_client: RestClient):
796
+ fake_project_id = uuid4()
797
+ fake_trajectory_id = str(uuid4())
798
+
799
+ with patch.object(admin_client.client, "post") as mock_post:
800
+ mock_response = MagicMock()
801
+ mock_response.raise_for_status.side_effect = Exception("404 Not Found")
802
+ mock_post.return_value = mock_response
803
+
804
+ with pytest.raises(
805
+ ProjectError, match="Error adding trajectory to project"
806
+ ):
807
+ admin_client.add_task_to_project(fake_project_id, fake_trajectory_id)
808
+
809
+ def test_create_project_with_http_error(self, admin_client: RestClient):
810
+ with patch.object(admin_client.client, "post") as mock_post:
811
+ mock_response = MagicMock()
812
+ mock_response.raise_for_status.side_effect = Exception("400 Bad Request")
813
+ mock_response.status_code = 400
814
+ mock_response.text = "Invalid project name"
815
+ mock_post.return_value = mock_response
816
+
817
+ with pytest.raises(ProjectError, match="Error creating project"):
818
+ admin_client.create_project("invalid project name")
819
+
820
+
821
+ class TestAsyncProjectOperations:
822
+ @pytest.fixture
823
+ def test_project_name(self):
824
+ return f"test-async-project-{int(time.time())}"
825
+
826
+ @pytest.mark.asyncio
827
+ async def test_acreate_project_success(
828
+ self, admin_client: RestClient, test_project_name: str
829
+ ):
830
+ project_id = await admin_client.acreate_project(test_project_name)
831
+ assert isinstance(project_id, str)
832
+ UUID(project_id)
833
+
834
+ @pytest.mark.asyncio
835
+ async def test_aget_project_by_name_success(
836
+ self, admin_client: RestClient, test_project_name: str
837
+ ):
838
+ created_project_id = await admin_client.acreate_project(test_project_name)
839
+ retrieved_project_id = await admin_client.aget_project_by_name(
840
+ test_project_name
841
+ )
842
+ assert str(retrieved_project_id) == created_project_id
843
+
844
+ @pytest.mark.asyncio
845
+ async def test_aget_project_by_name_not_found(self, admin_client: RestClient):
846
+ with pytest.raises(ProjectError, match="No project found with name"):
847
+ await admin_client.aget_project_by_name("non-existent-async-project-12345")
848
+
849
+ @pytest.mark.asyncio
850
+ async def test_aadd_task_to_project_success(
851
+ self, admin_client: RestClient, test_project_name: str, task_req: TaskRequest
852
+ ):
853
+ project_id = await admin_client.acreate_project(test_project_name)
854
+ trajectory_id = await admin_client.acreate_task(task_req)
855
+ await admin_client.aadd_task_to_project(UUID(project_id), trajectory_id)
856
+
857
+ @pytest.mark.asyncio
858
+ async def test_aadd_task_to_project_permission_denied(
859
+ self, admin_client: RestClient
860
+ ):
861
+ fake_project_id = uuid4()
862
+ fake_trajectory_id = str(uuid4())
863
+
864
+ with patch.object(admin_client.async_client, "post") as mock_post:
865
+ mock_response = MagicMock()
866
+ mock_response.raise_for_status.side_effect = Exception("403 Forbidden")
867
+ mock_response.status_code = 403
868
+ mock_post.return_value = mock_response
869
+
870
+ with pytest.raises(
871
+ ProjectError, match="Error adding trajectory to project: 403 Forbidden"
872
+ ):
873
+ await admin_client.aadd_task_to_project(
874
+ fake_project_id, fake_trajectory_id
875
+ )