From e3a6114c04dfccdeb94145bf8890327b9bb3bf84 Mon Sep 17 00:00:00 2001 From: Maciej Wilk Date: Thu, 7 May 2026 14:11:38 +0200 Subject: [PATCH 1/5] Create int tests for Reserved IPs #1 --- .../integration/networking/test_networking.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tests/integration/networking/test_networking.py b/tests/integration/networking/test_networking.py index f54907202..f62274ebd 100644 --- a/tests/integration/networking/test_networking.py +++ b/tests/integration/networking/test_networking.py @@ -1,3 +1,4 @@ +import ipaddress import json import re @@ -13,6 +14,28 @@ create_linode, create_linode_and_wait, ) +from tests.integration.sharegroups.fixtures import get_region # noqa: F401 + + +@pytest.fixture +def create_reserved_ip(request, get_region): + tags = getattr(request, "param", None) + region = get_region + command = BASE_CMDS["networking"] + [ + "reserved-ip-add", + "--region", + region, + "--json", + ] + + if tags: + command += ["--tags", tags] + + result = json.loads(exec_test_command(command))[0] + + yield result + + delete_target_id("networking", result["address"], "reserved-ip-delete") @pytest.fixture(scope="package") @@ -61,6 +84,18 @@ def has_shared_ip(linode_id: int, ip: str) -> bool: return False +def verify_reserved_ip(reserved_ip): + assert isinstance( + ipaddress.ip_address(reserved_ip["address"]), ipaddress.IPv4Address + ) + assert reserved_ip["type"] == "ipv4" + assert reserved_ip["public"] == True + assert reserved_ip["reserved"] == True + assert reserved_ip["linode_id"] is None + # TODO: To be clarified if it should be returned in CLI + # assert reserved_ip["assigned_entity"] is None + + def test_display_ips_for_available_linodes(test_linode_id): result = exec_test_command( BASE_CMDS["networking"] @@ -145,6 +180,53 @@ def test_allocate_additional_private_ipv4_address(test_linode_id): ) +@pytest.mark.parametrize( + "create_reserved_ip", ["test", None], indirect=True +) +def test_create_reserved_ip(create_reserved_ip): + reserved_ip = create_reserved_ip + verify_reserved_ip(reserved_ip) + + tags = reserved_ip["tags"] + assert tags == ["test"] if tags else tags == [] + + +def test_create_reserved_ip_wo_region_fail(): + with pytest.raises(RuntimeError) as exc_info: + exec_test_command( + BASE_CMDS["networking"] + [ + "reserved-ip-add", + "--json", + ] + ) + + assert "Request failed: 400" in exc_info.value.args[0] + assert "region is required" in exc_info.value.args[0] + + +@pytest.mark.parametrize( + "create_reserved_ip", ["test"], indirect=True +) +def test_update_reserved_ip_tags(create_reserved_ip): + reserved_ip = create_reserved_ip + verify_reserved_ip(reserved_ip) + assert reserved_ip["tags"] == ["test"] + + result = json.loads( + exec_test_command( + BASE_CMDS["networking"] + [ + "reserved-ip-update", + "--tags", + "updated", + reserved_ip["address"], + "--json", + ] + ) + )[0] + verify_reserved_ip(result) + assert result["tags"] == ["updated"] + + def test_share_ipv4_address( test_linode_id_shared_ipv4, monkeypatch: MonkeyPatch ): From 0041c67dda3ea67f4df0c46318d3e3d41852cb7b Mon Sep 17 00:00:00 2001 From: Maciej Wilk Date: Fri, 8 May 2026 11:44:59 +0200 Subject: [PATCH 2/5] Create int tests for Reserved IPs networking --- .../integration/networking/test_networking.py | 73 +++++++++++++++---- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/tests/integration/networking/test_networking.py b/tests/integration/networking/test_networking.py index f62274ebd..8049b428f 100644 --- a/tests/integration/networking/test_networking.py +++ b/tests/integration/networking/test_networking.py @@ -9,22 +9,23 @@ BASE_CMDS, delete_target_id, exec_test_command, + get_random_region_with_caps, ) from tests.integration.linodes.helpers import ( create_linode, create_linode_and_wait, + DEFAULT_REGION ) from tests.integration.sharegroups.fixtures import get_region # noqa: F401 @pytest.fixture -def create_reserved_ip(request, get_region): +def create_reserved_ip(request): tags = getattr(request, "param", None) - region = get_region command = BASE_CMDS["networking"] + [ "reserved-ip-add", "--region", - region, + DEFAULT_REGION, "--json", ] @@ -191,19 +192,6 @@ def test_create_reserved_ip(create_reserved_ip): assert tags == ["test"] if tags else tags == [] -def test_create_reserved_ip_wo_region_fail(): - with pytest.raises(RuntimeError) as exc_info: - exec_test_command( - BASE_CMDS["networking"] + [ - "reserved-ip-add", - "--json", - ] - ) - - assert "Request failed: 400" in exc_info.value.args[0] - assert "region is required" in exc_info.value.args[0] - - @pytest.mark.parametrize( "create_reserved_ip", ["test"], indirect=True ) @@ -227,6 +215,59 @@ def test_update_reserved_ip_tags(create_reserved_ip): assert result["tags"] == ["updated"] +# @pytest.mark.parametrize( +# "create_reserved_ip", ["test"], indirect=True +# ) +def test_create_reserved_ip_assigned(test_linode_id, create_reserved_ip): + linode_id = test_linode_id + reserved_ip = create_reserved_ip + verify_reserved_ip(reserved_ip) + + exec_test_command( + BASE_CMDS["networking"] + [ + "ip-assign", + "--assignments.linode_id", + linode_id, + "--assignments.address", + reserved_ip["address"], + "--region", + DEFAULT_REGION, + ] + ) + + result = json.loads( + exec_test_command( + BASE_CMDS["linodes"] + [ + "ip-view", + linode_id, + reserved_ip["address"], + "--json" + ] + ) + )[0] + assert result["address"] == reserved_ip["address"] + assert str(result["linode_id"]) == linode_id + assert result["reserved"] == True + # TODO: To be clarified if it should be returned in CLI + # assert result["tags"] == reserved_ip["tags"] + + +def test_get_reserved_ip_types(): + result = json.loads( + exec_test_command( + BASE_CMDS["networking"] + [ + "reserved-ip-types-list", + "--json" + ] + ) + )[0] + assert result["id"] == "reserved-ipv4" + assert result["label"] == "Reserved IPv4" + assert "hourly" in result["price"] + assert "monthly" in result["price"] + assert any(price != 0 for price in list(result["price"].values())) + + def test_share_ipv4_address( test_linode_id_shared_ipv4, monkeypatch: MonkeyPatch ): From 7d8729470d696777805a9e9c2ca5726fdc6d1085 Mon Sep 17 00:00:00 2001 From: Maciej Wilk Date: Fri, 8 May 2026 14:45:17 +0200 Subject: [PATCH 3/5] Add tests for reserved ip types, view and list + refactor existing --- tests/integration/linodes/helpers.py | 3 +- .../integration/networking/test_networking.py | 174 +++++++++++------- 2 files changed, 111 insertions(+), 66 deletions(-) diff --git a/tests/integration/linodes/helpers.py b/tests/integration/linodes/helpers.py index b07f0794a..f94667f14 100644 --- a/tests/integration/linodes/helpers.py +++ b/tests/integration/linodes/helpers.py @@ -1,4 +1,5 @@ import json +import os import time from tests.integration.helpers import ( @@ -7,7 +8,7 @@ ) DEFAULT_RANDOM_PASS = exec_test_command(["openssl", "rand", "-base64", "32"]) -DEFAULT_REGION = "us-ord" +DEFAULT_REGION = "pl-labkrk-2" if "devcloud" in os.getenv('LINODE_CLI_API_HOST') else "us-ord" DEFAULT_TEST_IMAGE = exec_test_command( [ diff --git a/tests/integration/networking/test_networking.py b/tests/integration/networking/test_networking.py index 8049b428f..4ceaf445c 100644 --- a/tests/integration/networking/test_networking.py +++ b/tests/integration/networking/test_networking.py @@ -7,9 +7,9 @@ from tests.integration.helpers import ( BASE_CMDS, + assert_headers_in_lines, delete_target_id, exec_test_command, - get_random_region_with_caps, ) from tests.integration.linodes.helpers import ( create_linode, @@ -19,6 +19,11 @@ from tests.integration.sharegroups.fixtures import get_region # noqa: F401 +RESERVED_IP_HEADERS = [ + "address", "type", "public", "rdns", "linode_id", "reserved", "tags" +] + + @pytest.fixture def create_reserved_ip(request): tags = getattr(request, "param", None) @@ -26,17 +31,18 @@ def create_reserved_ip(request): "reserved-ip-add", "--region", DEFAULT_REGION, - "--json", + "--text", + "--delimiter", + "," ] if tags: command += ["--tags", tags] - result = json.loads(exec_test_command(command))[0] - - yield result + headers, values = get_command_heads_and_vals(command) + yield headers, values - delete_target_id("networking", result["address"], "reserved-ip-delete") + delete_target_id("networking", values[0], "reserved-ip-delete") @pytest.fixture(scope="package") @@ -67,6 +73,14 @@ def test_linode_id_shared_ipv4(linode_cloud_firewall): delete_target_id(target="linodes", id=id) +def get_command_heads_and_vals(command): + result = exec_test_command(command).splitlines() + headers = [item for item in result[0].split(",")] + values = [item for item in result[1].split(",")] + + return headers, values + + def has_shared_ip(linode_id: int, ip: str) -> bool: shared_ips = json.loads( exec_test_command( @@ -87,12 +101,13 @@ def has_shared_ip(linode_id: int, ip: str) -> bool: def verify_reserved_ip(reserved_ip): assert isinstance( - ipaddress.ip_address(reserved_ip["address"]), ipaddress.IPv4Address + ipaddress.ip_address(reserved_ip[0]), ipaddress.IPv4Address ) - assert reserved_ip["type"] == "ipv4" - assert reserved_ip["public"] == True - assert reserved_ip["reserved"] == True - assert reserved_ip["linode_id"] is None + assert reserved_ip[1] == "ipv4" + assert reserved_ip[2] == "True" + assert reserved_ip[4] == DEFAULT_REGION + assert not reserved_ip[5] + assert reserved_ip[7] == "True" # TODO: To be clarified if it should be returned in CLI # assert reserved_ip["assigned_entity"] is None @@ -180,48 +195,47 @@ def test_allocate_additional_private_ipv4_address(test_linode_id): "ipv4,False,.*,[0-9][0-9][0-9][0-9][0-9][0-9][0-9]*", result ) - +@pytest.mark.smoke @pytest.mark.parametrize( "create_reserved_ip", ["test", None], indirect=True ) def test_create_reserved_ip(create_reserved_ip): - reserved_ip = create_reserved_ip + headers, reserved_ip = create_reserved_ip + assert_headers_in_lines(RESERVED_IP_HEADERS, [headers]) verify_reserved_ip(reserved_ip) - tags = reserved_ip["tags"] - assert tags == ["test"] if tags else tags == [] + tags = reserved_ip[-1] + assert tags == "test" if tags else tags == '' @pytest.mark.parametrize( "create_reserved_ip", ["test"], indirect=True ) def test_update_reserved_ip_tags(create_reserved_ip): - reserved_ip = create_reserved_ip - verify_reserved_ip(reserved_ip) - assert reserved_ip["tags"] == ["test"] + _, reserved_ip = create_reserved_ip + assert reserved_ip[-1] == "test" - result = json.loads( - exec_test_command( - BASE_CMDS["networking"] + [ - "reserved-ip-update", - "--tags", - "updated", - reserved_ip["address"], - "--json", - ] - ) - )[0] + result = exec_test_command( + BASE_CMDS["networking"] + [ + "reserved-ip-update", + "--tags", + "updated", + "--tags", + "updated2", + reserved_ip[0], + "--text", + "--no-headers", + "--delimiter", + "," + ] + ).split(",") verify_reserved_ip(result) - assert result["tags"] == ["updated"] + assert result[-1] == "updated updated2" -# @pytest.mark.parametrize( -# "create_reserved_ip", ["test"], indirect=True -# ) -def test_create_reserved_ip_assigned(test_linode_id, create_reserved_ip): +def test_create_reserved_ip_assigned(create_reserved_ip, test_linode_id): + _, reserved_ip = create_reserved_ip linode_id = test_linode_id - reserved_ip = create_reserved_ip - verify_reserved_ip(reserved_ip) exec_test_command( BASE_CMDS["networking"] + [ @@ -229,43 +243,73 @@ def test_create_reserved_ip_assigned(test_linode_id, create_reserved_ip): "--assignments.linode_id", linode_id, "--assignments.address", - reserved_ip["address"], + reserved_ip[0], "--region", DEFAULT_REGION, ] ) - result = json.loads( - exec_test_command( - BASE_CMDS["linodes"] + [ - "ip-view", - linode_id, - reserved_ip["address"], - "--json" - ] - ) - )[0] - assert result["address"] == reserved_ip["address"] - assert str(result["linode_id"]) == linode_id - assert result["reserved"] == True - # TODO: To be clarified if it should be returned in CLI - # assert result["tags"] == reserved_ip["tags"] + command = BASE_CMDS["linodes"] + [ + "ip-view", + linode_id, + reserved_ip[0], + "--text", + "--delimiter", + "," + ] + headers, values = get_command_heads_and_vals(command) + + assert_headers_in_lines(RESERVED_IP_HEADERS[:-1], [headers]) + # TODO: To be clarified if tags should be returned in CLI (currently it is) + # assert "tags" not in headers + assert values[0] == reserved_ip[0] + assert str(values[5]) == linode_id + assert values[7] == "True" def test_get_reserved_ip_types(): - result = json.loads( - exec_test_command( - BASE_CMDS["networking"] + [ - "reserved-ip-types-list", - "--json" - ] - ) - )[0] - assert result["id"] == "reserved-ipv4" - assert result["label"] == "Reserved IPv4" - assert "hourly" in result["price"] - assert "monthly" in result["price"] - assert any(price != 0 for price in list(result["price"].values())) + headers_exp = ["id", "label", "price.hourly", "price.monthly"] + command = BASE_CMDS["networking"] + [ + "reserved-ip-types-list", + "--text", + "--delimiter", + "," + ] + headers, values = get_command_heads_and_vals(command) + + assert_headers_in_lines(headers_exp, [headers]) + assert values[0] == "reserved-ipv4" + assert values[1] == "Reserved IPv4" + assert any(price != 0 for price in values[2:4]) + + +def test_get_reserved_ip_view(create_reserved_ip): + _, reserved_ip = create_reserved_ip + command = BASE_CMDS["networking"] + [ + "reserved-ip-view", + reserved_ip[0], + "--text", + "--delimiter", + "," + ] + headers, values = get_command_heads_and_vals(command) + + assert_headers_in_lines(RESERVED_IP_HEADERS, [headers]) + verify_reserved_ip(values) + + +def test_get_reserved_ips_list(create_reserved_ip): + result = exec_test_command( + BASE_CMDS["networking"] + [ + "reserved-ips-list", + "--text", + "--no-headers", + "--format", + "reserved" + ] + ).splitlines() + + assert all(item == "True" for item in result) def test_share_ipv4_address( From c38efc77dcc1abb57520d2b4030a72e7099ffaa2 Mon Sep 17 00:00:00 2001 From: Maciej Wilk Date: Fri, 8 May 2026 15:29:31 +0200 Subject: [PATCH 4/5] Extract fixtures.py for networking --- tests/integration/networking/fixtures.py | 69 +++++++++++++++++++ .../integration/networking/test_networking.py | 69 ++----------------- 2 files changed, 75 insertions(+), 63 deletions(-) create mode 100644 tests/integration/networking/fixtures.py diff --git a/tests/integration/networking/fixtures.py b/tests/integration/networking/fixtures.py new file mode 100644 index 000000000..9285783d6 --- /dev/null +++ b/tests/integration/networking/fixtures.py @@ -0,0 +1,69 @@ +import pytest + +from tests.integration.helpers import ( + BASE_CMDS, + delete_target_id, + exec_test_command, +) +from tests.integration.linodes.helpers import ( + create_linode, + create_linode_and_wait, + DEFAULT_REGION +) + + +@pytest.fixture +def create_reserved_ip(request): + tags = getattr(request, "param", None) + command = BASE_CMDS["networking"] + [ + "reserved-ip-add", + "--region", + DEFAULT_REGION, + "--text", + "--delimiter", + "," + ] + + if tags: + command += ["--tags", tags] + + headers, values = get_command_heads_and_vals(command) + yield headers, values + + delete_target_id("networking", values[0], "reserved-ip-delete") + + +@pytest.fixture(scope="package") +def test_linode_id(linode_cloud_firewall): + linode_id = create_linode_and_wait(firewall_id=linode_cloud_firewall) + + yield linode_id + + delete_target_id(target="linodes", id=linode_id) + + +@pytest.fixture(scope="package") +def test_linode_id_shared_ipv4(linode_cloud_firewall): + target_region = "us-mia" + + linode_ids = ( + create_linode( + test_region=target_region, firewall_id=linode_cloud_firewall + ), + create_linode( + test_region=target_region, firewall_id=linode_cloud_firewall + ), + ) + + yield linode_ids + + for id_num in linode_ids: + delete_target_id(target="linodes", id=id_num) + + +def get_command_heads_and_vals(command): + result = exec_test_command(command).splitlines() + headers = [item for item in result[0].split(",")] + values = [item for item in result[1].split(",")] + + return headers, values \ No newline at end of file diff --git a/tests/integration/networking/test_networking.py b/tests/integration/networking/test_networking.py index 4ceaf445c..3a52511c8 100644 --- a/tests/integration/networking/test_networking.py +++ b/tests/integration/networking/test_networking.py @@ -8,15 +8,15 @@ from tests.integration.helpers import ( BASE_CMDS, assert_headers_in_lines, - delete_target_id, exec_test_command, ) -from tests.integration.linodes.helpers import ( - create_linode, - create_linode_and_wait, - DEFAULT_REGION +from tests.integration.linodes.helpers import DEFAULT_REGION +from tests.integration.networking.fixtures import ( + create_reserved_ip, + get_command_heads_and_vals, + test_linode_id, + test_linode_id_shared_ipv4 ) -from tests.integration.sharegroups.fixtures import get_region # noqa: F401 RESERVED_IP_HEADERS = [ @@ -24,63 +24,6 @@ ] -@pytest.fixture -def create_reserved_ip(request): - tags = getattr(request, "param", None) - command = BASE_CMDS["networking"] + [ - "reserved-ip-add", - "--region", - DEFAULT_REGION, - "--text", - "--delimiter", - "," - ] - - if tags: - command += ["--tags", tags] - - headers, values = get_command_heads_and_vals(command) - yield headers, values - - delete_target_id("networking", values[0], "reserved-ip-delete") - - -@pytest.fixture(scope="package") -def test_linode_id(linode_cloud_firewall): - linode_id = create_linode_and_wait(firewall_id=linode_cloud_firewall) - - yield linode_id - - delete_target_id(target="linodes", id=linode_id) - - -@pytest.fixture(scope="package") -def test_linode_id_shared_ipv4(linode_cloud_firewall): - target_region = "us-mia" - - linode_ids = ( - create_linode( - test_region=target_region, firewall_id=linode_cloud_firewall - ), - create_linode( - test_region=target_region, firewall_id=linode_cloud_firewall - ), - ) - - yield linode_ids - - for id in linode_ids: - delete_target_id(target="linodes", id=id) - - -def get_command_heads_and_vals(command): - result = exec_test_command(command).splitlines() - headers = [item for item in result[0].split(",")] - values = [item for item in result[1].split(",")] - - return headers, values - - def has_shared_ip(linode_id: int, ip: str) -> bool: shared_ips = json.loads( exec_test_command( From 439a3e69dcaf1d5e59f572282b6e1c273892519b Mon Sep 17 00:00:00 2001 From: Maciej Wilk Date: Fri, 8 May 2026 15:30:50 +0200 Subject: [PATCH 5/5] Create test for tag creation with reserved_ipv4_addresses --- tests/integration/tags/test_tags.py | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/integration/tags/test_tags.py b/tests/integration/tags/test_tags.py index a51e13629..aa4d9b71e 100644 --- a/tests/integration/tags/test_tags.py +++ b/tests/integration/tags/test_tags.py @@ -8,6 +8,7 @@ exec_test_command, get_random_text, ) +from tests.integration.networking.fixtures import create_reserved_ip @pytest.fixture(scope="session") @@ -42,3 +43,35 @@ def test_fail_to_create_tag_shorter_than_three_char(): ) assert "Request failed: 400" in result assert "Length must be 3-50 characters" in result + + +def test_create_tag_for_reserved_ip(create_reserved_ip): + _, reserved_ip = create_reserved_ip + tag_name = get_random_text(5) + "-tag" + + exec_test_command( + BASE_CMDS["tags"] + + [ + "create", + "--label", + tag_name, + "--reserved_ipv4_addresses", + reserved_ip[0] + ] + ) + + result = exec_test_command( + BASE_CMDS["networking"] + [ + "reserved-ip-view", + reserved_ip[0], + "--text", + "--no-headers", + "--format", + "tags" + ] + ) + + assert result == tag_name + + # TODO: GET tags/{label} and DELETE tags/{label} missed or not implemented at all + # delete_target_id("tags", tag_name)