diff --git a/integration/Makefile b/integration/Makefile index 7627a755..a00278a1 100644 --- a/integration/Makefile +++ b/integration/Makefile @@ -26,6 +26,8 @@ PYTEST_ARGS ?= "-s" # Versions CONNECT_VERSIONS := \ + 2025.06.0 \ + 2025.05.0 \ 2025.04.0 \ 2025.03.0 \ 2025.02.0 \ diff --git a/integration/tests/posit/connect/fixtures.py b/integration/tests/posit/connect/fixtures.py new file mode 100644 index 00000000..6317c008 --- /dev/null +++ b/integration/tests/posit/connect/fixtures.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import random +import string + + +def name() -> str: + """Generate a unique name.""" + return "".join(random.choices(string.ascii_letters, k=8)) + + +def email() -> str: + """Generate a unique email address.""" + name = "".join(random.choices(string.ascii_lowercase, k=8)) + return f"{name}@example.com" + + +def username() -> str: + """Generate a unique username.""" + return "".join(random.choices(string.ascii_letters, k=8)) + + +def password() -> str: + """Generate a secure password.""" + characters = string.ascii_letters + string.digits + string.punctuation + return "".join(random.choices(characters, k=12)) # 12-character password diff --git a/integration/tests/posit/connect/oauth/test_associations.py b/integration/tests/posit/connect/oauth/test_associations.py index 6ec3759d..7584ee53 100644 --- a/integration/tests/posit/connect/oauth/test_associations.py +++ b/integration/tests/posit/connect/oauth/test_associations.py @@ -5,7 +5,7 @@ from posit import connect -from .. import CONNECT_VERSION +from .. import CONNECT_VERSION, fixtures @pytest.mark.skipif( @@ -17,7 +17,7 @@ class TestAssociations: def setup_class(cls): cls.client = connect.Client() cls.integration = cls.client.oauth.integrations.create( - name="example integration", + name=fixtures.name(), description="integration description", template="custom", config={ @@ -32,7 +32,7 @@ def setup_class(cls): ) cls.another_integration = cls.client.oauth.integrations.create( - name="another example integration", + name=fixtures.name(), description="another integration description", template="custom", config={ @@ -48,26 +48,19 @@ def setup_class(cls): # create content # requires full bundle deployment to produce an interactive content type - cls.content = cls.client.content.create(name="example-flask-minimal") + cls.content = cls.client.content.create(name=fixtures.name()) + # create bundle path = Path("../../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz") path = (Path(__file__).parent / path).resolve() bundle = cls.content.bundles.create(str(path)) + # deploy bundle task = bundle.deploy() task.wait_for() cls.content.oauth.associations.update(cls.integration["guid"]) - @classmethod - def teardown_class(cls): - cls.integration.delete() - cls.another_integration.delete() - assert len(cls.client.oauth.integrations.find()) == 0 - - cls.content.delete() - assert cls.client.content.count() == 0 - def test_find_by_integration(self): associations = self.integration.associations.find() assert len(associations) == 1 diff --git a/integration/tests/posit/connect/oauth/test_integrations.py b/integration/tests/posit/connect/oauth/test_integrations.py index 25720bb6..7812eeb9 100644 --- a/integration/tests/posit/connect/oauth/test_integrations.py +++ b/integration/tests/posit/connect/oauth/test_integrations.py @@ -3,7 +3,7 @@ from posit import connect -from .. import CONNECT_VERSION +from .. import CONNECT_VERSION, fixtures @pytest.mark.skipif( @@ -15,7 +15,7 @@ class TestIntegrations: def setup_class(cls): cls.client = connect.Client() cls.integration = cls.client.oauth.integrations.create( - name="example integration", + name=fixtures.name(), description="integration description", template="custom", config={ @@ -30,7 +30,7 @@ def setup_class(cls): ) cls.another_integration = cls.client.oauth.integrations.create( - name="another example integration", + name=fixtures.name(), description="another integration description", template="custom", config={ @@ -44,27 +44,21 @@ def setup_class(cls): }, ) - @classmethod - def teardown_class(cls): - cls.integration.delete() - cls.another_integration.delete() - assert len(cls.client.oauth.integrations.find()) == 0 - def test_get(self): result = self.client.oauth.integrations.get(self.integration["guid"]) assert result == self.integration def test_find(self): results = self.client.oauth.integrations.find() - assert len(results) == 2 - assert results[0] == self.integration - assert results[1] == self.another_integration + assert len(results) >= 2 + integration_guids = {integration["guid"] for integration in results} + assert self.integration["guid"] in integration_guids + assert self.another_integration["guid"] in integration_guids def test_create_update_delete(self): # create a new integration - integration = self.client.oauth.integrations.create( - name="new integration", + name=fixtures.name(), description="new integration description", template="custom", config={ @@ -82,7 +76,7 @@ def test_create_update_delete(self): assert created == integration all_integrations = self.client.oauth.integrations.find() - assert len(all_integrations) == 3 + assert len(all_integrations) >= 3 # update the new integration @@ -94,4 +88,4 @@ def test_create_update_delete(self): created.delete() all_integrations_after_delete = self.client.oauth.integrations.find() - assert len(all_integrations_after_delete) == 2 + assert len(all_integrations_after_delete) == len(all_integrations) - 1 diff --git a/integration/tests/posit/connect/test_bundles.py b/integration/tests/posit/connect/test_bundles.py index 0e613033..4b02e1eb 100644 --- a/integration/tests/posit/connect/test_bundles.py +++ b/integration/tests/posit/connect/test_bundles.py @@ -24,10 +24,6 @@ def setup_class(cls): ) cls.bundle_path = (Path(__file__).parent / bundle_path).resolve() - @classmethod - def teardown_class(cls): - cls.content.delete() - def test_create_bundle(self): """Test creating a bundle.""" bundle = self.content.bundles.create(str(self.bundle_path)) diff --git a/integration/tests/posit/connect/test_content.py b/integration/tests/posit/connect/test_content.py index 4b555824..b3eaccfc 100644 --- a/integration/tests/posit/connect/test_content.py +++ b/integration/tests/posit/connect/test_content.py @@ -5,7 +5,7 @@ from posit import connect -from . import CONNECT_VERSION +from . import CONNECT_VERSION, fixtures class TestContent: @@ -14,13 +14,9 @@ def setup_class(cls): cls.client = connect.Client() cls.content = cls.client.content.create() - @classmethod - def teardown_class(cls): - cls.content.delete() - assert cls.client.content.count() == 0 - def test_count(self): - assert self.client.content.count() == 1 + # Assert that count works. We don't care what the count is. + assert self.client.content.count() def test_get(self): assert self.client.content.get(self.content["guid"]) == self.content @@ -52,7 +48,7 @@ def test_content_item_owner_from_include(self): ) def test_restart(self): # create content - content = self.client.content.create(name="example-flask-minimal") + content = self.client.content.create(name=fixtures.name()) # create bundle path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz") path = (Path(__file__).parent / path).resolve() @@ -71,7 +67,7 @@ def test_restart(self): ) def test_render(self): # create content - content = self.client.content.create(name="example-quarto-minimal") + content = self.client.content.create(name=fixtures.name()) # create bundle path = Path("../../../resources/connect/bundles/example-quarto-minimal/bundle.tar.gz") path = (Path(__file__).parent / path).resolve() diff --git a/integration/tests/posit/connect/test_content_item_permissions.py b/integration/tests/posit/connect/test_content_item_permissions.py index 9f3ee04b..f8a8f2a3 100644 --- a/integration/tests/posit/connect/test_content_item_permissions.py +++ b/integration/tests/posit/connect/test_content_item_permissions.py @@ -5,6 +5,8 @@ from posit import connect +from .fixtures import email, name, password, username + if TYPE_CHECKING: from posit.connect.content import ContentItem from posit.connect.permissions import Permission @@ -16,42 +18,33 @@ class TestContentPermissions: @classmethod def setup_class(cls): cls.client = connect.Client() - cls.content = cls.client.content.create(name="example") + cls.content = cls.client.content.create(name=name()) - cls.user_aron = cls.client.users.create( - username="permission_aron", - email="permission_aron@example.com", - password="permission_s3cur3p@ssword", - ) - cls.user_bill = cls.client.users.create( - username="permission_bill", - email="permission_bill@example.com", - password="permission_s3cur3p@ssword", + cls.alice = cls.client.users.create( + username=name(), + email=email(), + password=password(), ) - cls.group_friends = cls.client.groups.create(name="Friends") - - @classmethod - def teardown_class(cls): - cls.content.delete() - assert cls.client.content.count() == 0 + cls.bob = cls.client.users.create( + username=username(), + email=email(), + password=password(), + ) - cls.group_friends.delete() - assert cls.client.groups.count() == 0 + cls.group = cls.client.groups.create(name=name()) def test_permissions_add_destroy(self): - assert self.client.groups.count() == 1 - assert self.client.users.count() == 3 assert self.content.permissions.find() == [] # Add permissions self.content.permissions.create( - principal_guid=self.user_aron["guid"], + principal_guid=self.alice["guid"], principal_type="user", role="viewer", ) self.content.permissions.create( - principal_guid=self.group_friends["guid"], + principal_guid=self.group["guid"], principal_type="group", role="owner", ) @@ -63,22 +56,22 @@ def assert_permissions_match_guids(permissions: list[Permission], objs_with_guid # Prove they have been added assert_permissions_match_guids( self.content.permissions.find(), - [self.user_aron, self.group_friends], + [self.alice, self.group], ) # Remove permissions (and from some that isn't an owner) - self.content.permissions.destroy(self.user_aron) + self.content.permissions.destroy(self.alice) with pytest.raises(ValueError): - self.content.permissions.destroy(self.user_bill) + self.content.permissions.destroy(self.bob) # Prove they have been removed assert_permissions_match_guids( self.content.permissions.find(), - [self.group_friends], + [self.group], ) # Remove the last permission - self.content.permissions.destroy(self.group_friends) + self.content.permissions.destroy(self.group) # Prove they have been removed assert self.content.permissions.find() == [] diff --git a/integration/tests/posit/connect/test_content_item_repository.py b/integration/tests/posit/connect/test_content_item_repository.py index 7911847d..18eebeb7 100644 --- a/integration/tests/posit/connect/test_content_item_repository.py +++ b/integration/tests/posit/connect/test_content_item_repository.py @@ -5,7 +5,7 @@ from posit.connect.content import ContentItem from posit.connect.repository import ContentItemRepository -from . import CONNECT_VERSION +from . import CONNECT_VERSION, fixtures class TestContentItemRepository: @@ -14,12 +14,7 @@ class TestContentItemRepository: @classmethod def setup_class(cls): cls.client = connect.Client() - cls.content = cls.client.content.create(name="example") - - @classmethod - def teardown_class(cls): - cls.content.delete() - assert cls.client.content.count() == 0 + cls.content = cls.client.content.create(name=fixtures.name()) @property def repo_repository(self): diff --git a/integration/tests/posit/connect/test_env.py b/integration/tests/posit/connect/test_env.py index 0d0f4083..4799ff38 100644 --- a/integration/tests/posit/connect/test_env.py +++ b/integration/tests/posit/connect/test_env.py @@ -1,21 +1,18 @@ from posit import connect +from . import fixtures + class TestEnvVars: @classmethod def setup_class(cls): cls.client = connect.Client() cls.content = cls.client.content.create( - name="Sample", + name=fixtures.name(), description="Simple sample content for testing", access_type="acl", ) - @classmethod - def teardown_class(cls): - cls.content.delete() - assert cls.client.content.count() == 0 - def test_clear(self): self.content.environment_variables.create("KEY", "value") assert self.content.environment_variables.find() == ["KEY"] diff --git a/integration/tests/posit/connect/test_environments.py b/integration/tests/posit/connect/test_environments.py index 5d4110f4..81883ea5 100644 --- a/integration/tests/posit/connect/test_environments.py +++ b/integration/tests/posit/connect/test_environments.py @@ -20,11 +20,6 @@ def setup_class(cls): cluster_name="Kubernetes", ) - @classmethod - def teardown_class(cls): - cls.environment.destroy() - assert len(cls.client.environments) == 0 - def test_find(self): uid = self.environment["guid"] environment = self.client.environments.find(uid) diff --git a/integration/tests/posit/connect/test_groups.py b/integration/tests/posit/connect/test_groups.py index 6c64dd81..0ac3a0a2 100644 --- a/integration/tests/posit/connect/test_groups.py +++ b/integration/tests/posit/connect/test_groups.py @@ -1,25 +1,26 @@ from posit import connect +from . import fixtures + class TestGroups: @classmethod def setup_class(cls): cls.client = connect.Client() - cls.item = cls.client.groups.create(name="Friends") - - @classmethod - def teardown_class(cls): - cls.item.delete() - assert cls.client.groups.count() == 0 + cls.item = cls.client.groups.create(name=fixtures.name()) def test_count(self): - assert self.client.groups.count() == 1 + # Assert that count works. We don't care what the count is. + assert self.client.groups.count() def test_get(self): assert self.client.groups.get(self.item["guid"]) def test_find(self): - assert self.client.groups.find() == [self.item] + groups = self.client.groups.find() + group_guids = {group["guid"] for group in groups} + assert self.item["guid"] in group_guids def test_find_one(self): - assert self.client.groups.find_one() == self.item + found_group = self.client.groups.find_one() + assert found_group is not None diff --git a/integration/tests/posit/connect/test_jobs.py b/integration/tests/posit/connect/test_jobs.py index 75c90530..f590b1c3 100644 --- a/integration/tests/posit/connect/test_jobs.py +++ b/integration/tests/posit/connect/test_jobs.py @@ -5,7 +5,7 @@ from posit import connect -from . import CONNECT_VERSION +from . import CONNECT_VERSION, fixtures @pytest.mark.skipif( @@ -16,12 +16,7 @@ class TestJobs: @classmethod def setup_class(cls): cls.client = connect.Client() - cls.content = cls.client.content.create(name="example-quarto-minimal") - - @classmethod - def teardown_class(cls): - cls.content.delete() - assert cls.client.content.count() == 0 + cls.content = cls.client.content.create(name=fixtures.name()) def test(self): content = self.content diff --git a/integration/tests/posit/connect/test_packages.py b/integration/tests/posit/connect/test_packages.py index 1d56c420..f4ee0e62 100644 --- a/integration/tests/posit/connect/test_packages.py +++ b/integration/tests/posit/connect/test_packages.py @@ -23,10 +23,6 @@ def setup_class(cls): task = bundle.deploy() task.wait_for() - @classmethod - def teardown_class(cls): - cls.content.delete() - def test(self): assert self.client.packages assert self.content.packages diff --git a/integration/tests/posit/connect/test_system.py b/integration/tests/posit/connect/test_system.py index 9caacb5b..b52d777e 100644 --- a/integration/tests/posit/connect/test_system.py +++ b/integration/tests/posit/connect/test_system.py @@ -7,7 +7,7 @@ from posit.connect.system import SystemRuntimeCache from posit.connect.tasks import Task -from . import CONNECT_VERSION +from . import CONNECT_VERSION, fixtures @pytest.mark.skipif( @@ -20,43 +20,31 @@ class TestSystem: @classmethod def setup_class(cls): cls.client = Client() - assert cls.client.content.count() == 0 - cls.content_item = cls.client.content.create(name="Content_A") + cls.content_item = cls.client.content.create(name=fixtures.name()) - # Copied from from integration/tests/posit/connect/test_packages.py - def _deploy_python_bundle(self): - path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz") - path = (Path(__file__).parent / path).resolve() - bundle = self.content_item.bundles.create(str(path)) - task = bundle.deploy() - task.wait_for() + def test_runtime_caches(self): + # Get current caches + caches = self.client.system.caches.runtime.find() + assert isinstance(caches, list) - def _remove_all_caches(self): - caches: list[SystemRuntimeCache] = self.client.system.caches.runtime.find() + # Remove all caches + caches = self.client.system.caches.runtime.find() for cache in caches: assert isinstance(cache, SystemRuntimeCache) none_val = cache.destroy(dry_run=True) assert none_val is None - task: Task = cache.destroy() + task = cache.destroy() assert isinstance(task, Task) task.wait_for() - assert len(self.client.system.caches.runtime.find()) == 0 - - @classmethod - def teardown_class(cls): - cls.content_item.delete() - assert cls.client.content.count() == 0 - - def test_runtime_caches(self): - # Get current caches - caches: list[SystemRuntimeCache] = self.client.system.caches.runtime.find() - assert isinstance(caches, list) - # Remove all caches - self._remove_all_caches() + assert len(self.client.system.caches.runtime.find()) == 0 # Deploy a new cache - self._deploy_python_bundle() + path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz") + path = (Path(__file__).parent / path).resolve() + bundle = self.content_item.bundles.create(str(path)) + task = bundle.deploy() + task.wait_for() # Check if the cache is deployed caches = self.client.system.caches.runtime.find() @@ -69,4 +57,13 @@ def test_runtime_caches(self): assert len(caches) > 0 # Remove all caches - self._remove_all_caches() + caches = self.client.system.caches.runtime.find() + for cache in caches: + assert isinstance(cache, SystemRuntimeCache) + none_val = cache.destroy(dry_run=True) + assert none_val is None + task = cache.destroy() + assert isinstance(task, Task) + task.wait_for() + + assert len(self.client.system.caches.runtime.find()) == 0 diff --git a/integration/tests/posit/connect/test_tags.py b/integration/tests/posit/connect/test_tags.py index 99e9deb4..d4827e9b 100644 --- a/integration/tests/posit/connect/test_tags.py +++ b/integration/tests/posit/connect/test_tags.py @@ -1,173 +1,166 @@ from posit import connect +from . import fixtures + class TestTags: @classmethod def setup_class(cls): cls.client = connect.Client() - cls.contentA = cls.client.content.create(name="Content_A") - cls.contentB = cls.client.content.create(name="Content_B") - cls.contentC = cls.client.content.create(name="Content_C") - - @classmethod - def teardown_class(cls): - assert len(cls.client.tags.find()) == 0 - cls.contentA.delete() - cls.contentB.delete() - cls.contentC.delete() - assert len(cls.client.content.find()) == 0 + cls.A = cls.client.content.create(name=fixtures.name()) + cls.B = cls.client.content.create(name=fixtures.name()) + cls.C = cls.client.content.create(name=fixtures.name()) def test_tags_find(self): tags = self.client.tags.find() assert len(tags) == 0 def test_tags_create_destroy(self): - tagA = self.client.tags.create(name="tagA") - tagB = self.client.tags.create(name="tagB") - tagC = self.client.tags.create(name="tagC", parent=tagA) + a = self.client.tags.create(name=fixtures.name()) + b = self.client.tags.create(name=fixtures.name()) + c = self.client.tags.create(name=fixtures.name(), parent=a) assert len(self.client.tags.find()) == 3 - tagA.destroy() - tagB.destroy() - ## Deleted when tag A is deleted - # tagC.destroy() + a.destroy() + b.destroy() assert len(self.client.tags.find()) == 0 def test_tag_descendants(self): - tagA = self.client.tags.create(name="tagA") - tagB = self.client.tags.create(name="tagB") - tagC = self.client.tags.create(name="tagC", parent=tagA) - tagD = self.client.tags.create(name="tagD", parent=tagC) + a = self.client.tags.create(name=fixtures.name()) + b = self.client.tags.create(name=fixtures.name()) + c = self.client.tags.create(name=fixtures.name(), parent=a) + d = self.client.tags.create(name=fixtures.name(), parent=c) - assert tagA.descendant_tags.find() == [tagC, tagD] + assert a.descendant_tags.find() == [c, d] - assert len(tagB.descendant_tags.find()) == 0 - assert tagC.descendant_tags.find() == [tagD] + assert len(b.descendant_tags.find()) == 0 + assert c.descendant_tags.find() == [d] # cleanup - tagA.destroy() - tagB.destroy() + a.destroy() + b.destroy() assert len(self.client.tags.find()) == 0 def test_tag_children(self): - tagA = self.client.tags.create(name="tagA_children") - tagB = self.client.tags.create(name="tagB_children") - tagC = self.client.tags.create(name="tagC_children", parent=tagA) - tagD = self.client.tags.create(name="tagD_children", parent=tagC) + a = self.client.tags.create(name=fixtures.name()) + b = self.client.tags.create(name=fixtures.name()) + c = self.client.tags.create(name=fixtures.name(), parent=a) + d = self.client.tags.create(name=fixtures.name(), parent=c) - assert tagA.child_tags.find() == [tagC] - assert tagB.child_tags.find() == [] - assert tagC.child_tags.find() == [tagD] + assert a.child_tags.find() == [c] + assert b.child_tags.find() == [] + assert c.child_tags.find() == [d] # cleanup - tagA.destroy() - tagB.destroy() + a.destroy() + b.destroy() assert len(self.client.tags.find()) == 0 def test_tag_parent(self): - tagA = self.client.tags.create(name="tagA_parent") - tagB = self.client.tags.create(name="tagB_parent") - tagC = self.client.tags.create(name="tagC_parent", parent=tagA) - tagD = self.client.tags.create(name="tagD_parent", parent=tagC) + a = self.client.tags.create(name=fixtures.name()) + b = self.client.tags.create(name=fixtures.name()) + c = self.client.tags.create(name=fixtures.name(), parent=a) + d = self.client.tags.create(name=fixtures.name(), parent=c) - assert tagA.parent_tag is None - assert tagB.parent_tag is None - assert tagC.parent_tag == tagA - assert tagD.parent_tag == tagC + assert a.parent_tag is None + assert b.parent_tag is None + assert c.parent_tag == a + assert d.parent_tag == c # cleanup - tagA.destroy() - tagB.destroy() + a.destroy() + b.destroy() assert len(self.client.tags.find()) == 0 def test_content_item_tags(self): - tagRoot = self.client.tags.create(name="tagRoot_content_item_tags") - tagA = self.client.tags.create(name="tagA_content_item_tags", parent=tagRoot) - tagB = self.client.tags.create(name="tagB_content_item_tags", parent=tagRoot) - tagC = self.client.tags.create(name="tagC_content_item_tags", parent=tagA) - tagD = self.client.tags.create(name="tagD_content_item_tags", parent=tagC) + root = self.client.tags.create(name=fixtures.name()) + a = self.client.tags.create(name=fixtures.name(), parent=root) + b = self.client.tags.create(name=fixtures.name(), parent=root) + c = self.client.tags.create(name=fixtures.name(), parent=a) + d = self.client.tags.create(name=fixtures.name(), parent=c) - assert len(self.contentA.tags.find()) == 0 + assert len(self.A.tags.find()) == 0 - self.contentA.tags.add(tagD) - self.contentA.tags.add(tagB) + self.A.tags.add(d) + self.A.tags.add(b) - # tagB, tagD, tagC (parent of tagD), tagA (parent of tagC) - # tagD + tagC - # tagRoot is considered a "category" and is "not a tag" - assert len(self.contentA.tags.find()) == 4 + # b, d, c (parent of d), a (parent of c) + # d + c + # root is considered a "category" and is "not a tag" + assert len(self.A.tags.find()) == 4 - # Removes tagB - self.contentA.tags.delete(tagB) - assert len(self.contentA.tags.find()) == 4 - 1 + # Removes b + self.A.tags.delete(b) + assert len(self.A.tags.find()) == 4 - 1 - # Removes tagC and tagD (parent of tagC) - self.contentA.tags.delete(tagC) - assert len(self.contentA.tags.find()) == 4 - 1 - 2 + # Removes c and d (parent of c) + self.A.tags.delete(c) + assert len(self.A.tags.find()) == 4 - 1 - 2 # cleanup - tagRoot.destroy() + root.destroy() assert len(self.client.tags.find()) == 0 def test_tag_content_items(self): - tagRoot = self.client.tags.create(name="tagRoot_tag_content_items") - tagA = self.client.tags.create(name="tagA_tag_content_items", parent=tagRoot) - tagB = self.client.tags.create(name="tagB_tag_content_items", parent=tagRoot) - tagC = self.client.tags.create(name="tagC_tag_content_items", parent=tagA) - tagD = self.client.tags.create(name="tagD_tag_content_items", parent=tagC) + root = self.client.tags.create(name=fixtures.name()) + a = self.client.tags.create(name=fixtures.name(), parent=root) + b = self.client.tags.create(name=fixtures.name(), parent=root) + c = self.client.tags.create(name=fixtures.name(), parent=a) + d = self.client.tags.create(name=fixtures.name(), parent=c) - assert len(tagA.content_items.find()) == 0 - assert len(tagB.content_items.find()) == 0 - assert len(tagC.content_items.find()) == 0 - assert len(tagD.content_items.find()) == 0 + assert len(a.content_items.find()) == 0 + assert len(b.content_items.find()) == 0 + assert len(c.content_items.find()) == 0 + assert len(d.content_items.find()) == 0 - self.contentA.tags.add(tagD) - self.contentA.tags.add(tagB) + self.A.tags.add(d) + self.A.tags.add(b) - self.contentB.tags.add(tagA) - self.contentB.tags.add(tagC) + self.B.tags.add(a) + self.B.tags.add(c) - self.contentC.tags.add(tagC) + self.C.tags.add(c) - assert len(self.contentA.tags.find()) == 4 - assert len(self.contentB.tags.find()) == 2 - assert len(self.contentC.tags.find()) == 2 + assert len(self.A.tags.find()) == 4 + assert len(self.B.tags.find()) == 2 + assert len(self.C.tags.find()) == 2 - assert len(tagA.content_items.find()) == 3 - assert len(tagB.content_items.find()) == 1 - assert len(tagC.content_items.find()) == 3 + assert len(a.content_items.find()) == 3 + assert len(b.content_items.find()) == 1 + assert len(c.content_items.find()) == 3 # Make sure unique content items are found - content_items = tagA.content_items.find() + content_items = a.content_items.find() assert len(content_items) == 3 content_item_guids = {content_item["guid"] for content_item in content_items} assert content_item_guids == { - self.contentA["guid"], - self.contentB["guid"], - self.contentC["guid"], + self.A["guid"], + self.B["guid"], + self.C["guid"], } # Update tag - tagD.update(name="tagD_updated") - assert tagD["name"] == "tagD_updated" - assert self.client.tags.get(tagD["id"])["name"] == "tagD_updated" + name = fixtures.name() + d.update(name=name) + assert d["name"] == name + assert self.client.tags.get(d["id"])["name"] == name - tagD.update(parent=tagB) - assert tagD["parent_id"] == tagB["id"] - assert self.client.tags.get(tagD["id"])["parent_id"] == tagB["id"] + d.update(parent=b) + assert d["parent_id"] == b["id"] + assert self.client.tags.get(d["id"])["parent_id"] == b["id"] # Cleanup - self.contentA.tags.delete(tagRoot) - self.contentB.tags.delete(tagRoot) - self.contentC.tags.delete(tagRoot) + self.A.tags.delete(root) + self.B.tags.delete(root) + self.C.tags.delete(root) - assert len(tagA.content_items.find()) == 0 - assert len(tagB.content_items.find()) == 0 - assert len(tagC.content_items.find()) == 0 - assert len(tagD.content_items.find()) == 0 + assert len(a.content_items.find()) == 0 + assert len(b.content_items.find()) == 0 + assert len(c.content_items.find()) == 0 + assert len(d.content_items.find()) == 0 - tagRoot.destroy() + root.destroy() assert len(self.client.tags.find()) == 0 diff --git a/integration/tests/posit/connect/test_users.py b/integration/tests/posit/connect/test_users.py index efb3c72c..4fa54763 100644 --- a/integration/tests/posit/connect/test_users.py +++ b/integration/tests/posit/connect/test_users.py @@ -1,97 +1,76 @@ from posit import connect +from . import fixtures + class TestUser: @classmethod def setup_class(cls): cls.client = client = connect.Client() - - # Play nicely with other tests - cls.existing_user_count = client.users.count() - - cls.aron = client.users.create( - username="aron", - email="aron@example.com", - password="s3cur3p@ssword", + cls.alice = client.users.create( + username=fixtures.username(), + email=fixtures.email(), + password=fixtures.password(), ) - cls.bill = client.users.create( - username="bill", - email="bill@example.com", - password="s3cur3p@ssword", + cls.bob = client.users.create( + username=fixtures.username(), + email=fixtures.email(), + password=fixtures.password(), ) - cls.cole = client.users.create( - username="cole", - email="cole@example.com", - password="s3cur3p@ssword", + cls.carol = client.users.create( + username=fixtures.username(), + email=fixtures.email(), + password=fixtures.password(), ) def test_lock(self): - for user in (self.aron, self.bill, self.cole): + for user in (self.alice, self.bob, self.carol): user.lock() assert len(self.client.users.find(account_status="locked")) == 1 user.unlock() + assert len(self.client.users.find(account_status="locked")) == 0 def test_count(self): - # aron, bill, cole, and me (and existing user) - assert self.client.users.count() == 3 + self.existing_user_count + # Assert that count works. We don't care what the count is. + assert self.client.users.count() def test_find(self): - assert self.client.users.find(prefix="aron") == [self.aron] - assert self.client.users.find(prefix="bill") == [self.bill] - assert self.client.users.find(prefix="cole") == [self.cole] + assert self.client.users.find(prefix=self.alice["username"]) == [self.alice] + assert self.client.users.find(prefix=self.bob["username"]) == [self.bob] + assert self.client.users.find(prefix=self.carol["username"]) == [self.carol] def test_find_one(self): - assert self.client.users.find_one(prefix="aron") == self.aron - assert self.client.users.find_one(prefix="bill") == self.bill - assert self.client.users.find_one(prefix="cole") == self.cole + assert self.client.users.find_one(prefix=self.alice["username"]) == self.alice + assert self.client.users.find_one(prefix=self.bob["username"]) == self.bob + assert self.client.users.find_one(prefix=self.carol["username"]) == self.carol def test_get(self): - assert self.client.users.get(self.aron["guid"]) == self.aron - assert self.client.users.get(self.bill["guid"]) == self.bill - assert self.client.users.get(self.cole["guid"]) == self.cole - - # Also tests Groups.members - def test_user_group_interactions(self): - try: - test_group = self.client.groups.create(name="UnitFriends") + assert self.client.users.get(self.alice["guid"]) == self.alice + assert self.client.users.get(self.bob["guid"]) == self.bob + assert self.client.users.get(self.carol["guid"]) == self.carol - # `Group.members.count()` - assert test_group.members.count() == 0 + def test_members(self): + group = self.client.groups.create(name=fixtures.name()) - # `Group.members.add()` - test_group.members.add(self.bill) - # `User.groups.add()` - assert test_group.members.count() == 1 - self.cole.groups.add(test_group) - assert test_group.members.count() == 2 + # Add members to the group + group.members.add(self.bob) - # `Group.members.find()` - group_users = test_group.members.find() - assert len(group_users) == 2 - assert group_users[0]["guid"] == self.bill["guid"] - assert group_users[1]["guid"] == self.cole["guid"] + # Assign group to member + self.carol.groups.add(group) - # `User.group.find()` - bill_groups = self.bill.groups.find() - assert len(bill_groups) == 1 - assert bill_groups[0]["guid"] == test_group["guid"] + # Assert group members + members = group.members.find() + assert len(members) == 2 + assert members[0]["guid"] == self.bob["guid"] + assert members[1]["guid"] == self.carol["guid"] - # `Group.members.delete()` - test_group.members.delete(self.bill) - assert test_group.members.count() == 1 + # Assert group members through user + assert self.bob.groups.find()[0]["guid"] == group["guid"] - # `User.groups.delete()` - self.cole.groups.delete(test_group) - assert test_group.members.count() == 0 - - finally: - groups = self.client.groups.find(prefix="UnitFriends") - if len(groups) > 0: - test_group = groups[0] - test_group.delete() - - assert len(self.client.groups.find(prefix="UnitFriends")) == 0 + # Remove member from group + group.members.delete(self.bob) + assert group.members.count() == 1 class TestUserContent: @@ -100,20 +79,16 @@ class TestUserContent: @classmethod def setup_class(cls): cls.client = client = connect.Client() - cls.me = me = client.me - cls.content = me.content.create( - name="Sample", + cls.me = client.me + cls.content = client.me.content.create( + name=fixtures.name(), description="Simple sample content for testing", access_type="acl", ) - @classmethod - def teardown_class(cls): - assert cls.content.delete() is None - assert cls.me.content.count() == 0 - def test_count(self): - assert self.me.content.count() == 1 + # Assert that count works. We don't care what the count is. + assert self.me.content.count() >= 1 def test_find(self): assert self.me.content.find() @@ -123,11 +98,9 @@ def test_find_one(self): def test_multiple_users(self): user = self.client.users.create( - username="example", - email="example@example.com", - password="s3cur3p@ssword", + username=fixtures.username(), + email=fixtures.email(), + password=fixtures.password(), ) - # assert filtering limits to the provided user. - assert self.me.content.find_one() == self.content + assert user.content.find_one() is None - user.lock() diff --git a/integration/tests/posit/connect/test_vanities.py b/integration/tests/posit/connect/test_vanities.py index b79d43cc..43ae05ce 100644 --- a/integration/tests/posit/connect/test_vanities.py +++ b/integration/tests/posit/connect/test_vanities.py @@ -1,3 +1,5 @@ +import uuid + from posit import connect @@ -6,12 +8,9 @@ class TestVanities: def setup_class(cls): cls.client = connect.Client() - @classmethod - def teardown_class(cls): - assert cls.client.content.count() == 0 - def test_all(self): - content = self.client.content.create(name="example") + unique_name = f"example-{uuid.uuid4().hex[:8]}" + content = self.client.content.create(name=unique_name) # None by default vanities = self.client.vanities.all() @@ -31,7 +30,8 @@ def test_all(self): assert len(vanities) == 0 def test_property(self): - content = self.client.content.create(name="example") + unique_name = f"example-{uuid.uuid4().hex[:8]}" + content = self.client.content.create(name=unique_name) # None by default assert content.vanity is None @@ -51,7 +51,8 @@ def test_property(self): content.delete() def test_destroy(self): - content = self.client.content.create(name="example") + unique_name = f"example-{uuid.uuid4().hex[:8]}" + content = self.client.content.create(name=unique_name) # None by default assert content.vanity is None diff --git a/src/posit/connect/users.py b/src/posit/connect/users.py index a925df90..042a04cb 100644 --- a/src/posit/connect/users.py +++ b/src/posit/connect/users.py @@ -362,7 +362,7 @@ def create(self, **attributes: Unpack[CreateUser]) -> User: ... email="jdoe@example.com", ... first_name="John", ... last_name="Doe", - ... password="s3cur3p@ssword", + ... password=fixtures.password(), ... user_role="viewer", ... ) diff --git a/tests/posit/connect/test_client.py b/tests/posit/connect/test_client.py index be6fe9f9..ee40ce10 100644 --- a/tests/posit/connect/test_client.py +++ b/tests/posit/connect/test_client.py @@ -219,6 +219,7 @@ def test_me_request(self): con = Client(api_key="12345", url="https://connect.example/") assert con.me["username"] == "carlos12" + @responses.activate def test_request(self, MockSession): api_key = "12345" url = "https://connect.example.com" @@ -234,35 +235,45 @@ def test_get(self, MockSession): url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.get("/foo") - client.session.get.assert_called_once_with("https://connect.example.com/__api__/foo") + MockSession.return_value.get.assert_called_once_with( + "https://connect.example.com/__api__/foo" + ) def test_post(self, MockSession): api_key = "12345" url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.post("/foo") - client.session.post.assert_called_once_with("https://connect.example.com/__api__/foo") + MockSession.return_value.post.assert_called_once_with( + "https://connect.example.com/__api__/foo" + ) def test_put(self, MockSession): api_key = "12345" url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.put("/foo") - client.session.put.assert_called_once_with("https://connect.example.com/__api__/foo") + MockSession.return_value.put.assert_called_once_with( + "https://connect.example.com/__api__/foo" + ) def test_patch(self, MockSession): api_key = "12345" url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.patch("/foo") - client.session.patch.assert_called_once_with("https://connect.example.com/__api__/foo") + MockSession.return_value.patch.assert_called_once_with( + "https://connect.example.com/__api__/foo" + ) def test_delete(self, MockSession): api_key = "12345" url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.delete("/foo") - client.session.delete.assert_called_once_with("https://connect.example.com/__api__/foo") + MockSession.return_value.delete.assert_called_once_with( + "https://connect.example.com/__api__/foo" + ) class TestClientOAuth: diff --git a/tests/posit/connect/test_hooks.py b/tests/posit/connect/test_hooks.py index 62a061aa..e058013e 100644 --- a/tests/posit/connect/test_hooks.py +++ b/tests/posit/connect/test_hooks.py @@ -77,5 +77,8 @@ def test_deprecation_warning(): ) c = Client("https://connect.example", "12345") - with pytest.warns(DeprecationWarning): + with pytest.warns( + DeprecationWarning, + match="https://connect.example/__api__/v0 is deprecated and will be removed in a future version of Connect. Please upgrade `posit-sdk` in order to use the new APIs.", + ): c.get("v0")