Skip to content
Merged
Changes from 3 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
252 changes: 252 additions & 0 deletions test/integration/models/sharegroups/test_sharegroups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
import datetime
from test.integration.conftest import get_region
from test.integration.helpers import (
get_test_label,
)

import pytest

from linode_api4.objects import (
Image,
ImageShareGroup,
ImageShareGroupImagesToAdd,
ImageShareGroupImageToAdd,
ImageShareGroupImageToUpdate,
ImageShareGroupMemberToAdd,
ImageShareGroupMemberToUpdate,
ImageShareGroupToken,
)


def wait_for_image_status(
test_linode_client, image_id, expected_status, timeout=180, interval=5
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step timed out for me locally. Can we increase the timeout?

):
import time

get_image = test_linode_client.load(Image, image_id)
timer = 0
while get_image.status != expected_status and timer < timeout:
time.sleep(interval)
timer += interval
get_image = test_linode_client.load(Image, image_id)
if timer >= timeout:
raise TimeoutError(
f"Created image did not reach status '{expected_status}' within {timeout} seconds."
)


@pytest.fixture(scope="class")
def sample_linode(test_linode_client, e2e_test_firewall):
client = test_linode_client
region = get_region(client, {"Linodes", "Cloud Firewall"}, site_type="core")
label = get_test_label(length=8)

linode_instance, password = client.linode.instance_create(
"g6-nanode-1",
region,
image="linode/debian12",
label=label + "_modlinode",
)
yield linode_instance
linode_instance.delete()


@pytest.fixture(scope="class")
def create_image_id(test_linode_client, sample_linode):
create_image = test_linode_client.images.create(
sample_linode.disks[0],
label="linode-api4python-test-image-sharing-image",
)
wait_for_image_status(test_linode_client, create_image.id, "available")
yield create_image.id
create_image.delete()


@pytest.fixture(scope="function")
def share_group_id(test_linode_client):
group_label = get_test_label(8) + "_sharegroup_api4_test"
group = test_linode_client.sharegroups.create_sharegroup(
label=group_label,
description="Test api4python",
)
yield group.id
group.delete()


def test_get_share_groups(test_linode_client):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test fails for me since a share group is not created before running it. Can you add a step to create a share group before listing them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, let me add it

response = test_linode_client.sharegroups()
sharegroups_list = response.lists[0]
assert len(sharegroups_list) > 0
assert sharegroups_list[0].api_endpoint == "/images/sharegroups/{id}"
assert sharegroups_list[0].id > 0
assert sharegroups_list[0].description != ""
assert isinstance(sharegroups_list[0].images_count, int)
assert sharegroups_list[0].is_suspended == False
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use 'is False' or 'assert not sharegroups_list[0].is_suspended' instead of '== False' for boolean comparisons to follow Python best practices.

Suggested change
assert sharegroups_list[0].is_suspended == False
assert not sharegroups_list[0].is_suspended

Copilot uses AI. Check for mistakes.
assert sharegroups_list[0].label != ""
assert isinstance(sharegroups_list[0].members_count, int)
assert sharegroups_list[0].uuid != ""
assert isinstance(sharegroups_list[0].created, datetime.date)
assert isinstance(sharegroups_list[0].updated, datetime.date)
assert not sharegroups_list[0].expiry


def test_add_update_remove_share_group(test_linode_client):
group_label = get_test_label(8) + "_sharegroup_api4_test"
share_group = test_linode_client.sharegroups.create_sharegroup(
label=group_label,
description="Test api4python create",
)
assert share_group.api_endpoint == "/images/sharegroups/{id}"
assert share_group.id > 0
assert share_group.description == "Test api4python create"
assert isinstance(share_group.images_count, int)
assert share_group.is_suspended == False
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use 'is False' or 'assert not share_group.is_suspended' instead of '== False' for boolean comparisons to follow Python best practices.

Copilot uses AI. Check for mistakes.
assert share_group.label == group_label
assert isinstance(share_group.members_count, int)
assert share_group.uuid != ""
assert isinstance(share_group.created, datetime.date)
assert not share_group.updated
assert not share_group.expiry

load_share_group = test_linode_client.load(ImageShareGroup, share_group.id)
assert load_share_group.id == share_group.id
assert load_share_group.description == "Test api4python create"

load_share_group.label = "Updated Sharegroup Label"
load_share_group.description = "Test update description"
load_share_group.save()
load_share_group_after_update = test_linode_client.load(
ImageShareGroup, share_group.id
)
assert load_share_group_after_update.id == share_group.id
assert load_share_group_after_update.label == "Updated Sharegroup Label"
assert (
load_share_group_after_update.description == "Test update description"
)

share_group.delete()
with pytest.raises(RuntimeError) as err:
test_linode_client.load(ImageShareGroup, share_group.id)
assert "[404] Not found" in str(err.value)


def test_add_get_update_revoke_image_to_share_group(
test_linode_client, create_image_id, share_group_id
):
share_group = test_linode_client.load(ImageShareGroup, share_group_id)
add_image_response = share_group.add_images(
ImageShareGroupImagesToAdd(
images=[
ImageShareGroupImageToAdd(id=create_image_id),
]
)
)
assert 0 < len(add_image_response)
assert (
add_image_response[0].image_sharing.shared_by.sharegroup_id
== share_group.id
)
assert (
add_image_response[0].image_sharing.shared_by.source_image_id
== create_image_id
)

get_response = share_group.get_image_shares()
assert 0 < len(get_response)
assert (
get_response[0].image_sharing.shared_by.sharegroup_id == share_group.id
)
assert (
get_response[0].image_sharing.shared_by.source_image_id
== create_image_id
)
assert get_response[0].description == ""

update_response = share_group.update_image_share(
ImageShareGroupImageToUpdate(
image_share_id=get_response[0].id, description="Description update"
)
)
assert update_response.description == "Description update"

share_groups_by_image_id_response = (
test_linode_client.sharegroups.sharegroups_by_image_id(create_image_id)
)
assert 0 < len(share_groups_by_image_id_response.lists)
assert share_groups_by_image_id_response.lists[0][0].id == share_group.id

share_group.revoke_image_share(get_response[0].id)
get_after_revoke_response = share_group.get_image_shares()
assert len(get_after_revoke_response) == 0


def test_list_tokens(test_linode_client):
response = test_linode_client.sharegroups.tokens()
assert response.page_endpoint == "images/sharegroups/tokens"
assert len(response.lists[0]) >= 0


def test_create_token_to_own_share_group_error(test_linode_client):
group_label = get_test_label(8) + "_sharegroup_api4_test"
response_create_share_group = (
test_linode_client.sharegroups.create_sharegroup(
label=group_label,
description="Test api4python create",
)
)
with pytest.raises(RuntimeError) as err:
test_linode_client.sharegroups.create_token(
response_create_share_group.uuid
)
assert "[400] valid_for_sharegroup_uuid" in str(err.value)
assert "You may not create a token for your own sharegroup" in str(
err.value
)

response_create_share_group.delete()


def test_get_invalid_token(test_linode_client):
with pytest.raises(RuntimeError) as err:
test_linode_client.load(ImageShareGroupToken, "36b0-4d52_invalid")
assert "[404] Not found" in str(err.value)


def test_try_to_add_member_invalid_token(test_linode_client, share_group_id):
share_group = test_linode_client.load(ImageShareGroup, share_group_id)
with pytest.raises(RuntimeError) as err:
share_group.add_member(
ImageShareGroupMemberToAdd(
token="notExistingToken",
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The token value 'notExistingToken' uses camelCase which is inconsistent with Python naming conventions. Consider using 'not_existing_token' or 'invalid_token' instead.

Copilot uses AI. Check for mistakes.
label="New Member",
)
)
assert "[500] Invalid token format" in str(err.value)


def test_list_share_group_members(test_linode_client, share_group_id):
share_group = test_linode_client.load(ImageShareGroup, share_group_id)
response = share_group.get_members()
assert 0 == len(response)


def test_try_to_get_update_revoke_share_group_member_by_invalid_token(
test_linode_client, share_group_id
):
share_group = test_linode_client.load(ImageShareGroup, share_group_id)
with pytest.raises(RuntimeError) as err:
share_group.get_member("notExistingToken")
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The token value 'notExistingToken' uses camelCase which is inconsistent with Python naming conventions. Consider using 'not_existing_token' or 'invalid_token' for consistency.

Copilot uses AI. Check for mistakes.
assert "[404] Not found" in str(err.value)

with pytest.raises(RuntimeError) as err:
share_group.update_member(
ImageShareGroupMemberToUpdate(
token_uuid="notExistingToken",
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The token value 'notExistingToken' uses camelCase which is inconsistent with Python naming conventions. Consider using 'not_existing_token' or 'invalid_token' for consistency.

Copilot uses AI. Check for mistakes.
label="Update Member",
)
)
assert "[404] Not found" in str(err.value)

with pytest.raises(RuntimeError) as err:
share_group.remove_member("notExistingToken")
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The token value 'notExistingToken' uses camelCase which is inconsistent with Python naming conventions. Consider using 'not_existing_token' or 'invalid_token' for consistency.

Copilot uses AI. Check for mistakes.
assert "[404] Not found" in str(err.value)
Loading