Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions integration/tests/posit/connect/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,43 @@
"with-connect",
]

# The 2026.04+ Connect images moved to a new base image that contains one
# versioned Python installation but does not enable it in the default config.
_CONNECT_PYTHON_VERSIONS = {
"2026.04": "3.14.4",
"2026.05": "3.14.5",
"2026.06": "3.14.6",
"2026.07": "3.14.6",
"2026.08": "3.14.7",
}

# Posit Connect license file to mount into each container (gitignored).
_LICENSE = os.environ.get("LICENSE", "./license.lic")


def _connect_env(connect_version: str) -> list[str]:
"""Return Connect configuration overrides for the current image family."""
release = ".".join(connect_version.split(".")[:2])
python_version = _CONNECT_PYTHON_VERSIONS.get(release)
if python_version is None:
return []

return [
"--env",
"CONNECT_PYTHON_ENABLED=true",
"--env",
f"CONNECT_PYTHON_EXECUTABLE=/opt/python/{python_version}/bin/python",
"--env",
"CONNECT_PYTHON_VERSIONMATCHING=nearest",
"--env",
"CONNECT_PYTHON_ENVIRONMENTMANAGEMENT=true",
"--env",
"CONNECT_SERVER_ALLOWRUNTIMECACHEMANAGEMENT=true",
"--env",
"CONNECT_METRICS_INSTRUMENTATION=true",
]


def _free_port() -> int:
with socket.socket() as s:
s.bind(("", 0))
Expand All @@ -55,6 +88,7 @@ def fresh_connect():
"--license",
_LICENSE,
"--quiet",
*_connect_env(version),
],
capture_output=True,
text=True,
Expand Down
118 changes: 60 additions & 58 deletions integration/tests/posit/connect/test_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ def test_get(self):
assert key in item
assert item[key] == self.content[key]
if CONNECT_VERSION >= version.parse("2024.06.0"):
# get() always includes owner, tags, and vanity_url. Owner data is always present in
# all content, tags and vanity_url are only present if explicitly set in the content.
# Owner data is always present. Newer Connect versions include an empty tags field,
# while older versions omit it when no tags are set.
assert "owner" in item
assert "tags" not in item
assert item.get("tags", []) == []
assert "vanity_url" not in item

def test_find(self):
Expand Down Expand Up @@ -63,17 +63,18 @@ def test_content_item_owner_from_include(self):
def test_restart(self):
# create content
content = self.client.content.create(name="example-flask-minimal")
# create bundle
path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz")
path = (Path(__file__).parent / path).resolve()
bundle = content.bundles.create(str(path))
# deploy bundle
task = bundle.deploy()
task.wait_for()
# restart
content.restart()
# delete content
content.delete()
try:
# create bundle
path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz")
path = (Path(__file__).parent / path).resolve()
bundle = content.bundles.create(str(path))
# deploy bundle
task = bundle.deploy()
task.wait_for()
# restart
content.restart()
finally:
content.delete()

@pytest.mark.skipif(
CONNECT_VERSION <= version.parse("2023.01.1"),
Expand Down Expand Up @@ -102,32 +103,33 @@ def test_render(self):
def test_get_lockfile(self):
# create content
content = self.client.content.create(name="example-flask-lockfile-test")
# create bundle with Python requirements
path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz")
path = (Path(__file__).parent / path).resolve()
bundle = content.bundles.create(str(path))
# deploy bundle
task = bundle.deploy()
task.wait_for()
# get lockfile
lockfile = content.get_lockfile()
# verify lockfile metadata
assert lockfile.generated_by is not None
assert isinstance(lockfile.generated_by, str)
assert len(lockfile.generated_by) > 0
# verify python version was parsed
assert lockfile.python_version is not None
assert isinstance(lockfile.python_version, str)
assert len(lockfile.python_version) > 0
# verify lockfile content
assert lockfile.text is not None
assert isinstance(lockfile.text, str)
assert len(lockfile.text) > 0
# lockfile should contain package information
# The flask bundle has Flask as a dependency
assert "flask" in lockfile.text.lower() or "Flask" in lockfile.text
# delete content
content.delete()
try:
# create bundle with Python requirements
path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz")
path = (Path(__file__).parent / path).resolve()
bundle = content.bundles.create(str(path))
# deploy bundle
task = bundle.deploy()
task.wait_for()
# get lockfile
lockfile = content.get_lockfile()
# verify lockfile metadata
assert lockfile.generated_by is not None
assert isinstance(lockfile.generated_by, str)
assert len(lockfile.generated_by) > 0
# verify python version was parsed
assert lockfile.python_version is not None
assert isinstance(lockfile.python_version, str)
assert len(lockfile.python_version) > 0
# verify lockfile content
assert lockfile.text is not None
assert isinstance(lockfile.text, str)
assert len(lockfile.text) > 0
# lockfile should contain package information
# The flask bundle has Flask as a dependency
assert "flask" in lockfile.text.lower() or "Flask" in lockfile.text
finally:
content.delete()

@pytest.mark.skipif(
CONNECT_VERSION < version.parse("2025.12.0"),
Expand All @@ -140,21 +142,21 @@ def test_get_lockfile_version_check(self):
# Since we skip this test on older versions, we just verify
# that the method exists and is callable on supported versions
content = self.client.content.create(name="example-version-check")
path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz")
path = (Path(__file__).parent / path).resolve()
bundle = content.bundles.create(str(path))
task = bundle.deploy()
task.wait_for()

# Verify the method exists and is callable
assert hasattr(content, "get_lockfile")
assert callable(content.get_lockfile)

# Call it to ensure no version errors on supported versions
lockfile = content.get_lockfile()
assert lockfile.generated_by is not None
assert lockfile.python_version is not None
assert lockfile.text is not None

# delete content
content.delete()
try:
path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz")
path = (Path(__file__).parent / path).resolve()
bundle = content.bundles.create(str(path))
task = bundle.deploy()
task.wait_for()

# Verify the method exists and is callable
assert hasattr(content, "get_lockfile")
assert callable(content.get_lockfile)

# Call it to ensure no version errors on supported versions
lockfile = content.get_lockfile()
assert lockfile.generated_by is not None
assert lockfile.python_version is not None
assert lockfile.text is not None
finally:
content.delete()
10 changes: 8 additions & 2 deletions integration/tests/posit/connect/test_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ def test_get(self):
assert self.client.groups.get(self.group["guid"])

def test_find(self):
assert self.client.groups.find() == [self.group]
groups = self.client.groups.find()
assert len(groups) == 1
for key, value in self.group.items():
assert groups[0][key] == value

def test_find_one(self):
assert self.client.groups.find_one() == self.group
group = self.client.groups.find_one()
assert group is not None
for key, value in self.group.items():
assert group[key] == value
4 changes: 2 additions & 2 deletions integration/tests/posit/connect/test_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ def teardown_class(cls):
cls.content.delete()

def test(self):
assert self.client.packages
assert self.content.packages
assert list(self.client.packages)
assert list(self.content.packages)

def test_find_by(self):
package = self.client.packages.find_by(name="flask")
Expand Down
6 changes: 4 additions & 2 deletions integration/tests/posit/connect/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ def test_user_group_interactions(self):
# `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"]
assert {user["guid"] for user in group_users} == {
self.bill["guid"],
self.cole["guid"],
}

# `User.group.find()`
bill_groups = self.bill.groups.find()
Expand Down
Loading