From 36867af5b42cfb48932cace0e63d03bd8565450c Mon Sep 17 00:00:00 2001 From: Pawel Snoch Date: Mon, 22 Dec 2025 12:18:19 +0100 Subject: [PATCH 1/4] Create integration tests for share groups - part 1 --- .../models/sharegroups/test_sharegroups.py | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 test/integration/models/sharegroups/test_sharegroups.py diff --git a/test/integration/models/sharegroups/test_sharegroups.py b/test/integration/models/sharegroups/test_sharegroups.py new file mode 100644 index 000000000..336cfbc08 --- /dev/null +++ b/test/integration/models/sharegroups/test_sharegroups.py @@ -0,0 +1,122 @@ +import datetime + +from test.integration.helpers import ( + get_test_label, +) + +import pytest + +from linode_api4.objects import ( + Image, + ImageShareGroup, + ImageShareGroupToken, +) + + +def wait_for_image_status(test_linode_client, image_id, expected_status, timeout=180, interval=5): + 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="function") +def create_image_id(test_linode_client, linode_for_legacy_interface_tests): + # TODO: list disks by linode_for_legacy_interface_tests.id + create_image = test_linode_client.images.create(disks[0], label="linode-api4python-test-image-sharing-image") + wait_for_image_status(test_linode_client, create_image.id, "available") + yield create_image.id + + +def test_get_share_groups(test_linode_client): + 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 + 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" + response_create = test_linode_client.sharegroups.create_sharegroup( + label=group_label, + description="Test api4python create", + ) + assert response_create.api_endpoint == '/images/sharegroups/{id}' + assert response_create.id > 0 + assert response_create.description == 'Test api4python create' + assert isinstance(response_create.images_count, int) + assert response_create.is_suspended == False + assert response_create.label == group_label + assert isinstance(response_create.members_count, int) + assert response_create.uuid != '' + assert isinstance(response_create.created, datetime.date) + assert not response_create.updated + assert not response_create.expiry + + # TODO: update sharegroup label or description + + response_get = test_linode_client.load(ImageShareGroup, response_create.id) + assert response_get.id == response_create.id + assert response_get.description == 'Test api4python create' + + response_create.delete() + with pytest.raises(RuntimeError) as err: + test_linode_client.load(ImageShareGroup, response_create.id) + assert "[404] Not found" in str(err.value) + + +def test_create_and_list_share_groups_by_image_id(test_linode_client, create_image_id): + 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", + ) + + response_create_share_group.sharegroups_by_image_id(image_id=create_image_id) + # TODO: Add assertions + + response_create_share_group.delete() + + +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) From b2bf5d94ba9e35b4996df7f6ec469373f118b62b Mon Sep 17 00:00:00 2001 From: Pawel Snoch Date: Tue, 13 Jan 2026 15:25:33 +0100 Subject: [PATCH 2/4] Create test test_try_to_add_member_invalid_token --- .../models/sharegroups/test_sharegroups.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/integration/models/sharegroups/test_sharegroups.py b/test/integration/models/sharegroups/test_sharegroups.py index 336cfbc08..a24f3de43 100644 --- a/test/integration/models/sharegroups/test_sharegroups.py +++ b/test/integration/models/sharegroups/test_sharegroups.py @@ -9,6 +9,7 @@ from linode_api4.objects import ( Image, ImageShareGroup, + ImageShareGroupMemberToAdd, ImageShareGroupToken, ) @@ -36,6 +37,16 @@ def create_image_id(test_linode_client, linode_for_legacy_interface_tests): yield create_image.id +@pytest.fixture(scope="function") +def share_group_id(test_linode_client): + group_label = get_test_label(8) + "_sharegroup_api4_test" + response = test_linode_client.sharegroups.create_sharegroup( + label=group_label, + description="Test api4python", + ) + yield response.id + + def test_get_share_groups(test_linode_client): response = test_linode_client.sharegroups() sharegroups_list = response.lists[0] @@ -120,3 +131,17 @@ 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", + label="New Member", + ) + ) + assert "[500] Invalid token format" in str(err.value) + share_group.delete() + From 744761b9807215dc8c8f85bfa21c280d987d8464 Mon Sep 17 00:00:00 2001 From: Pawel Snoch Date: Thu, 15 Jan 2026 15:49:43 +0100 Subject: [PATCH 3/4] Update integration tests for private image sharing feature --- .../models/sharegroups/test_sharegroups.py | 201 +++++++++++++----- 1 file changed, 153 insertions(+), 48 deletions(-) diff --git a/test/integration/models/sharegroups/test_sharegroups.py b/test/integration/models/sharegroups/test_sharegroups.py index a24f3de43..70e491e65 100644 --- a/test/integration/models/sharegroups/test_sharegroups.py +++ b/test/integration/models/sharegroups/test_sharegroups.py @@ -1,5 +1,5 @@ import datetime - +from test.integration.conftest import get_region from test.integration.helpers import ( get_test_label, ) @@ -9,12 +9,18 @@ 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): +def wait_for_image_status( + test_linode_client, image_id, expected_status, timeout=180, interval=5 +): import time get_image = test_linode_client.load(Image, image_id) @@ -29,36 +35,56 @@ def wait_for_image_status(test_linode_client, image_id, expected_status, timeout ) -@pytest.fixture(scope="function") -def create_image_id(test_linode_client, linode_for_legacy_interface_tests): - # TODO: list disks by linode_for_legacy_interface_tests.id - create_image = test_linode_client.images.create(disks[0], label="linode-api4python-test-image-sharing-image") +@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" - response = test_linode_client.sharegroups.create_sharegroup( + group = test_linode_client.sharegroups.create_sharegroup( label=group_label, description="Test api4python", ) - yield response.id + yield group.id + group.delete() def test_get_share_groups(test_linode_client): 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].api_endpoint == "/images/sharegroups/{id}" assert sharegroups_list[0].id > 0 - assert sharegroups_list[0].description != '' + assert sharegroups_list[0].description != "" assert isinstance(sharegroups_list[0].images_count, int) assert sharegroups_list[0].is_suspended == False - assert sharegroups_list[0].label != '' + assert sharegroups_list[0].label != "" assert isinstance(sharegroups_list[0].members_count, int) - assert sharegroups_list[0].uuid != '' + 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 @@ -66,63 +92,116 @@ def test_get_share_groups(test_linode_client): def test_add_update_remove_share_group(test_linode_client): group_label = get_test_label(8) + "_sharegroup_api4_test" - response_create = test_linode_client.sharegroups.create_sharegroup( + share_group = test_linode_client.sharegroups.create_sharegroup( label=group_label, description="Test api4python create", ) - assert response_create.api_endpoint == '/images/sharegroups/{id}' - assert response_create.id > 0 - assert response_create.description == 'Test api4python create' - assert isinstance(response_create.images_count, int) - assert response_create.is_suspended == False - assert response_create.label == group_label - assert isinstance(response_create.members_count, int) - assert response_create.uuid != '' - assert isinstance(response_create.created, datetime.date) - assert not response_create.updated - assert not response_create.expiry - - # TODO: update sharegroup label or description - - response_get = test_linode_client.load(ImageShareGroup, response_create.id) - assert response_get.id == response_create.id - assert response_get.description == 'Test api4python create' - - response_create.delete() + 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 + 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, response_create.id) + test_linode_client.load(ImageShareGroup, share_group.id) assert "[404] Not found" in str(err.value) -def test_create_and_list_share_groups_by_image_id(test_linode_client, create_image_id): - 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", +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 ) - response_create_share_group.sharegroups_by_image_id(image_id=create_image_id) - # TODO: Add assertions + 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 == "" - response_create_share_group.delete() + 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 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", + 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) + 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) + assert "You may not create a token for your own sharegroup" in str( + err.value + ) response_create_share_group.delete() @@ -143,5 +222,31 @@ def test_try_to_add_member_invalid_token(test_linode_client, share_group_id): ) ) assert "[500] Invalid token format" in str(err.value) - share_group.delete() + +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") + assert "[404] Not found" in str(err.value) + + with pytest.raises(RuntimeError) as err: + share_group.update_member( + ImageShareGroupMemberToUpdate( + token_uuid="notExistingToken", + label="Update Member", + ) + ) + assert "[404] Not found" in str(err.value) + + with pytest.raises(RuntimeError) as err: + share_group.remove_member("notExistingToken") + assert "[404] Not found" in str(err.value) From c19136bb1bf8ecdc4044282736474e33153678c1 Mon Sep 17 00:00:00 2001 From: Pawel Snoch Date: Thu, 15 Jan 2026 18:48:06 +0100 Subject: [PATCH 4/4] Apply code review sugestions --- .../models/sharegroups/test_sharegroups.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/test/integration/models/sharegroups/test_sharegroups.py b/test/integration/models/sharegroups/test_sharegroups.py index 70e491e65..9c66bad90 100644 --- a/test/integration/models/sharegroups/test_sharegroups.py +++ b/test/integration/models/sharegroups/test_sharegroups.py @@ -19,7 +19,7 @@ def wait_for_image_status( - test_linode_client, image_id, expected_status, timeout=180, interval=5 + test_linode_client, image_id, expected_status, timeout=360, interval=5 ): import time @@ -44,7 +44,7 @@ def sample_linode(test_linode_client, e2e_test_firewall): linode_instance, password = client.linode.instance_create( "g6-nanode-1", region, - image="linode/debian12", + image="linode/alpine3.19", label=label + "_modlinode", ) yield linode_instance @@ -73,7 +73,7 @@ def share_group_id(test_linode_client): group.delete() -def test_get_share_groups(test_linode_client): +def test_get_share_groups(test_linode_client, share_group_id): response = test_linode_client.sharegroups() sharegroups_list = response.lists[0] assert len(sharegroups_list) > 0 @@ -81,12 +81,11 @@ def test_get_share_groups(test_linode_client): 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 + assert not sharegroups_list[0].is_suspended 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 @@ -100,7 +99,7 @@ def test_add_update_remove_share_group(test_linode_client): 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 + assert not share_group.is_suspended assert share_group.label == group_label assert isinstance(share_group.members_count, int) assert share_group.uuid != "" @@ -217,7 +216,7 @@ def test_try_to_add_member_invalid_token(test_linode_client, share_group_id): with pytest.raises(RuntimeError) as err: share_group.add_member( ImageShareGroupMemberToAdd( - token="notExistingToken", + token="not_existing_token", label="New Member", ) ) @@ -235,18 +234,18 @@ def test_try_to_get_update_revoke_share_group_member_by_invalid_token( ): share_group = test_linode_client.load(ImageShareGroup, share_group_id) with pytest.raises(RuntimeError) as err: - share_group.get_member("notExistingToken") + share_group.get_member("not_existing_token") assert "[404] Not found" in str(err.value) with pytest.raises(RuntimeError) as err: share_group.update_member( ImageShareGroupMemberToUpdate( - token_uuid="notExistingToken", + token_uuid="not_existing_token", label="Update Member", ) ) assert "[404] Not found" in str(err.value) with pytest.raises(RuntimeError) as err: - share_group.remove_member("notExistingToken") + share_group.remove_member("not_existing_token") assert "[404] Not found" in str(err.value)