diff --git a/dev-requirements.txt b/dev-requirements.txt index 3aa82cdd..04ca562b 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,7 +1,7 @@ -pytest==8.3.3 +pytest==8.3.4 flake8==7.1.1 pytest-timeout==2.3.1 -pytest-asyncio==0.21.2 +pytest-asyncio==0.25.2 requests==2.32.3 -httpx==0.27.2 # version 0.24.1 is required by httpx_ws -httpx_ws==0.6.2 +httpx==0.28.1 +httpx_ws==0.7.1 diff --git a/tests/api/routes/compute/test_capabilities.py b/tests/api/routes/compute/test_capabilities.py index e6ad64b3..071c629f 100644 --- a/tests/api/routes/compute/test_capabilities.py +++ b/tests/api/routes/compute/test_capabilities.py @@ -29,27 +29,29 @@ from gns3server.utils.path import get_default_project_directory pytestmark = pytest.mark.asyncio -async def test_get(app: FastAPI, compute_client: AsyncClient, windows_platform) -> None: +class TestCapabilitiesRoutes: - response = await compute_client.get(app.url_path_for("compute:get_capabilities")) - assert response.status_code == status.HTTP_200_OK - assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'docker', 'iou'], - 'version': __version__, - 'platform': sys.platform, - 'cpus': psutil.cpu_count(logical=True), - 'memory': psutil.virtual_memory().total, - 'disk_size': psutil.disk_usage(get_default_project_directory()).total, - } + async def test_get(self, app: FastAPI, compute_client: AsyncClient, windows_platform) -> None: + + response = await compute_client.get(app.url_path_for("compute:get_capabilities")) + assert response.status_code == status.HTTP_200_OK + assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'docker', 'iou'], + 'version': __version__, + 'platform': sys.platform, + 'cpus': psutil.cpu_count(logical=True), + 'memory': psutil.virtual_memory().total, + 'disk_size': psutil.disk_usage(get_default_project_directory()).total, + } -async def test_get_on_gns3vm(app: FastAPI, compute_client: AsyncClient, on_gns3vm) -> None: + async def test_get_on_gns3vm(self, app: FastAPI, compute_client: AsyncClient, on_gns3vm) -> None: - response = await compute_client.get(app.url_path_for("compute:get_capabilities")) - assert response.status_code == status.HTTP_200_OK - assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'docker', 'iou'], - 'version': __version__, - 'platform': sys.platform, - 'cpus': psutil.cpu_count(logical=True), - 'memory': psutil.virtual_memory().total, - 'disk_size': psutil.disk_usage(get_default_project_directory()).total, - } + response = await compute_client.get(app.url_path_for("compute:get_capabilities")) + assert response.status_code == status.HTTP_200_OK + assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'docker', 'iou'], + 'version': __version__, + 'platform': sys.platform, + 'cpus': psutil.cpu_count(logical=True), + 'memory': psutil.virtual_memory().total, + 'disk_size': psutil.disk_usage(get_default_project_directory()).total, + } diff --git a/tests/api/routes/compute/test_cloud_nodes.py b/tests/api/routes/compute/test_cloud_nodes.py index b29f8a2b..18014eb9 100644 --- a/tests/api/routes/compute/test_cloud_nodes.py +++ b/tests/api/routes/compute/test_cloud_nodes.py @@ -28,154 +28,193 @@ from gns3server.compute.project import Project pytestmark = pytest.mark.asyncio -@pytest_asyncio.fixture(scope="function") -async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, on_gns3vm) -> dict: +class TestCloudNodesRoutes: - with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): - response = await compute_client.post(app.url_path_for("compute:create_cloud", project_id=compute_project.id), - json={"name": "Cloud 1"}) - assert response.status_code == status.HTTP_201_CREATED - return response.json() + @pytest_asyncio.fixture + async def vm(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project, on_gns3vm) -> dict: + + with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): + response = await compute_client.post(app.url_path_for("compute:create_cloud", project_id=compute_project.id), + json={"name": "Cloud 1"}) + assert response.status_code == status.HTTP_201_CREATED + return response.json() -async def test_cloud_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: + async def test_cloud_create( + self, app: FastAPI, + compute_client: AsyncClient, + compute_project: Project + ) -> None: - with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): - response = await compute_client.post(app.url_path_for("compute:create_cloud", project_id=compute_project.id), - json={"name": "Cloud 1"}) - assert response.status_code == 201 - assert response.json()["name"] == "Cloud 1" - assert response.json()["project_id"] == compute_project.id + with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): + response = await compute_client.post(app.url_path_for("compute:create_cloud", project_id=compute_project.id), + json={"name": "Cloud 1"}) + assert response.status_code == 201 + assert response.json()["name"] == "Cloud 1" + assert response.json()["project_id"] == compute_project.id -async def test_get_cloud(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: + async def test_get_cloud( + self, app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: - response = await compute_client.get(app.url_path_for("compute:get_cloud", project_id=vm["project_id"], node_id=vm["node_id"])) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "Cloud 1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["status"] == "started" - - -async def test_cloud_nio_create_udp(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: - - params = {"type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1"} - - url = app.url_path_for("compute:create_cloud_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_cloud_nio_update_udp(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: - - params = {"type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1"} - - url = app.url_path_for("compute:create_cloud_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - await compute_client.post(url, json=params) - - params["filters"] = {} - url = app.url_path_for("compute:create_cloud_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - response = await compute_client.put(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_cloud_delete_nio(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: - - params = {"type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1"} - - url = app.url_path_for("compute:create_cloud_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - await compute_client.post(url, json=params) - - url = app.url_path_for("compute:delete_cloud_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): - response = await compute_client.delete(url) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_cloud_delete(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: - - response = await compute_client.delete(app.url_path_for("compute:delete_cloud", project_id=vm["project_id"], node_id=vm["node_id"])) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_cloud_update(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - response = await compute_client.put(app.url_path_for("compute:update_cloud", project_id=vm["project_id"], node_id=vm["node_id"]), - json={"name": "test"}) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - - -async def test_cloud_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "capture_file_name": "test.pcap", - "data_link_type": "DLT_EN10MB" - } - - with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.start_capture") as mock: - response = await compute_client.post(app.url_path_for("compute:start_cloud_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0"), - json=params) + response = await compute_client.get(app.url_path_for("compute:get_cloud", project_id=vm["project_id"], node_id=vm["node_id"])) assert response.status_code == status.HTTP_200_OK - assert mock.called - assert "test.pcap" in response.json()["pcap_file_path"] + assert response.json()["name"] == "Cloud 1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["status"] == "started" -async def test_cloud_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_cloud_nio_create_udp( + self, app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: - with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.stop_capture") as mock: - response = await compute_client.post(app.url_path_for("compute:stop_cloud_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0")) + params = {"type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1"} + + url = app.url_path_for("compute:create_cloud_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + async def test_cloud_nio_update_udp( + self, app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: + + params = {"type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1"} + + url = app.url_path_for("compute:create_cloud_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + await compute_client.post(url, json=params) + + params["filters"] = {} + url = app.url_path_for("compute:create_cloud_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + response = await compute_client.put(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + async def test_cloud_delete_nio( + self, app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: + + params = {"type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1"} + + url = app.url_path_for("compute:create_cloud_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + await compute_client.post(url, json=params) + + url = app.url_path_for("compute:delete_cloud_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): + response = await compute_client.delete(url) assert response.status_code == status.HTTP_204_NO_CONTENT - assert mock.called -# @pytest.mark.asyncio -# async def test_cloud_pcap(compute_api, vm, compute_project): -# -# from itertools import repeat -# stream = repeat(42, times=10) -# -# with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.get_nio"): -# with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file", return_value=stream): -# response = await compute_api.get("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"])) -# assert response.status_code == 200 -# + async def test_cloud_delete( + self, app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: + + response = await compute_client.delete(app.url_path_for("compute:delete_cloud", project_id=vm["project_id"], node_id=vm["node_id"])) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_cloud_update( + self, app: FastAPI, + compute_client: AsyncClient, + vm: dict + ) -> None: + + response = await compute_client.put(app.url_path_for("compute:update_cloud", project_id=vm["project_id"], node_id=vm["node_id"]), + json={"name": "test"}) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + + + async def test_cloud_start_capture( + self, app: FastAPI, + compute_client: AsyncClient, + vm: dict + ) -> None: + + params = { + "capture_file_name": "test.pcap", + "data_link_type": "DLT_EN10MB" + } + + with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.start_capture") as mock: + response = await compute_client.post(app.url_path_for("compute:start_cloud_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0"), + json=params) + assert response.status_code == status.HTTP_200_OK + assert mock.called + assert "test.pcap" in response.json()["pcap_file_path"] + + + async def test_cloud_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.stop_capture") as mock: + response = await compute_client.post(app.url_path_for("compute:stop_cloud_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0")) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called + + + # @pytest.mark.asyncio + # async def test_cloud_pcap(self, compute_api, vm, compute_project): + # + # from itertools import repeat + # stream = repeat(42, times=10) + # + # with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.get_nio"): + # with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file", return_value=stream): + # response = await compute_api.get("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"])) + # assert response.status_code == 200 + # diff --git a/tests/api/routes/compute/test_compute.py b/tests/api/routes/compute/test_compute.py index 45325aad..aa70ff6b 100644 --- a/tests/api/routes/compute/test_compute.py +++ b/tests/api/routes/compute/test_compute.py @@ -26,41 +26,48 @@ from gns3server.compute.project import Project pytestmark = pytest.mark.asyncio -async def test_udp_allocation(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: +class TestComputeRoutes: - response = await compute_client.post(app.url_path_for("compute:allocate_udp_port", project_id=compute_project.id), json={}) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()['udp_port'] is not None + async def test_udp_allocation( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project + ) -> None: + + response = await compute_client.post(app.url_path_for("compute:allocate_udp_port", project_id=compute_project.id), json={}) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()['udp_port'] is not None -async def test_interfaces(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_interfaces(self, app: FastAPI, compute_client: AsyncClient) -> None: - response = await compute_client.get(app.url_path_for("compute:network_interfaces")) - assert response.status_code == status.HTTP_200_OK - assert isinstance(response.json(), list) + response = await compute_client.get(app.url_path_for("compute:network_interfaces")) + assert response.status_code == status.HTTP_200_OK + assert isinstance(response.json(), list) -async def test_version_output(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_version_output(self, app: FastAPI, compute_client: AsyncClient) -> None: - response = await compute_client.get(app.url_path_for("compute:compute_version")) - assert response.status_code == status.HTTP_200_OK - assert response.json() == {'version': __version__} + response = await compute_client.get(app.url_path_for("compute:compute_version")) + assert response.status_code == status.HTTP_200_OK + assert response.json() == {'version': __version__} -async def test_compute_authentication(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_compute_authentication(self, app: FastAPI, compute_client: AsyncClient) -> None: - response = await compute_client.get(app.url_path_for("compute:compute_version"), auth=("admin", "invalid_password")) - assert response.status_code == status.HTTP_401_UNAUTHORIZED + response = await compute_client.get(app.url_path_for("compute:compute_version"), auth=("admin", "invalid_password")) + assert response.status_code == status.HTTP_401_UNAUTHORIZED -# @pytest.mark.asyncio -# async def test_debug_output(compute_api): -# -# response = await compute_api.get('/debug') -# assert response.status_code == 200 + # @pytest.mark.asyncio + # async def test_debug_output(compute_api): + # + # response = await compute_api.get('/debug') + # assert response.status_code == 200 -async def test_statistics_output(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_statistics_output(self, app: FastAPI, compute_client: AsyncClient) -> None: - response = await compute_client.get(app.url_path_for("compute:compute_statistics")) - assert response.status_code == status.HTTP_200_OK + response = await compute_client.get(app.url_path_for("compute:compute_statistics")) + assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/compute/test_docker_nodes.py b/tests/api/routes/compute/test_docker_nodes.py index f8c7935e..b36c2965 100644 --- a/tests/api/routes/compute/test_docker_nodes.py +++ b/tests/api/routes/compute/test_docker_nodes.py @@ -25,285 +25,299 @@ from unittest.mock import patch from gns3server.compute.project import Project -pytestmark = [pytest.mark.asyncio] +pytestmark = pytest.mark.asyncio -@pytest.fixture -def base_params() -> dict: - """Return standard parameters""" +class TestDockerNodesRoutes: - params = { - "name": "DOCKER-TEST-1", - "image": "nginx", - "start_command": "nginx-daemon", - "adapters": 2, - "environment": "YES=1\nNO=0", - "console_type": "telnet", - "console_resolution": "1280x1024", - "extra_hosts": "test:127.0.0.1" - } - return params + @pytest.fixture + def base_params(self) -> dict: + """Return standard parameters""" + + params = { + "name": "DOCKER-TEST-1", + "image": "nginx", + "start_command": "nginx-daemon", + "adapters": 2, + "environment": "YES=1\nNO=0", + "console_type": "telnet", + "console_resolution": "1280x1024", + "extra_hosts": "test:127.0.0.1" + } + return params -# @pytest.yield_fixture(autouse=True) -# def mock_connection(): -# -# docker = Docker.instance() -# docker._connected = True -# docker._connector = MagicMock() -# yield -# Docker._instance = None + # @pytest.yield_fixture(autouse=True) + # def mock_connection(): + # + # docker = Docker.instance() + # docker._connected = True + # docker._connector = MagicMock() + # yield + # Docker._instance = None -@pytest_asyncio.fixture -async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> dict: + @pytest_asyncio.fixture + async def vm( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict + ) -> dict: - with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): - with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): - with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="exited"): - response = await compute_client.post(app.url_path_for("compute:create_docker_node", project_id=compute_project.id), - json=base_params) - assert response.status_code == status.HTTP_201_CREATED - return response.json() + with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): + with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): + with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="exited"): + response = await compute_client.post(app.url_path_for("compute:create_docker_node", project_id=compute_project.id), + json=base_params) + assert response.status_code == status.HTTP_201_CREATED + return response.json() -async def test_docker_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> None: + async def test_docker_create( + self, app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict + ) -> None: - with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): - with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): - response = await compute_client.post( - app.url_path_for("compute:create_docker_node", project_id=compute_project.id), json=base_params - ) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "DOCKER-TEST-1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["container_id"] == "8bd8153ea8f5" - assert response.json()["image"] == "nginx:latest" - assert response.json()["adapters"] == 2 - assert response.json()["environment"] == "YES=1\nNO=0" - assert response.json()["console_resolution"] == "1280x1024" - assert response.json()["extra_hosts"] == "test:127.0.0.1" + with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): + with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): + response = await compute_client.post( + app.url_path_for("compute:create_docker_node", project_id=compute_project.id), json=base_params + ) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "DOCKER-TEST-1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["container_id"] == "8bd8153ea8f5" + assert response.json()["image"] == "nginx:latest" + assert response.json()["adapters"] == 2 + assert response.json()["environment"] == "YES=1\nNO=0" + assert response.json()["console_resolution"] == "1280x1024" + assert response.json()["extra_hosts"] == "test:127.0.0.1" -@pytest.mark.parametrize( - "name, status_code", - ( - ("valid-name.com", status.HTTP_201_CREATED), - ("42name", status.HTTP_201_CREATED), - ("424242", status.HTTP_409_CONFLICT), - ("name42", status.HTTP_201_CREATED), - ("name.424242", status.HTTP_409_CONFLICT), - ("-name", status.HTTP_409_CONFLICT), - ("name%-test", status.HTTP_409_CONFLICT), - ("x" * 63, status.HTTP_201_CREATED), - ("x" * 64, status.HTTP_409_CONFLICT), - (("x" * 62 + ".") * 4, status.HTTP_201_CREATED), - ("xx" + ("x" * 62 + ".") * 4, status.HTTP_409_CONFLICT), - ), -) -async def test_docker_create_with_invalid_name( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - base_params: dict, - name: str, - status_code: int -) -> None: + @pytest.mark.parametrize( + "name, status_code", + ( + ("valid-name.com", status.HTTP_201_CREATED), + ("42name", status.HTTP_201_CREATED), + ("424242", status.HTTP_409_CONFLICT), + ("name42", status.HTTP_201_CREATED), + ("name.424242", status.HTTP_409_CONFLICT), + ("-name", status.HTTP_409_CONFLICT), + ("name%-test", status.HTTP_409_CONFLICT), + ("x" * 63, status.HTTP_201_CREATED), + ("x" * 64, status.HTTP_409_CONFLICT), + (("x" * 62 + ".") * 4, status.HTTP_201_CREATED), + ("xx" + ("x" * 62 + ".") * 4, status.HTTP_409_CONFLICT), + ), + ) + async def test_docker_create_with_invalid_name( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict, + name: str, + status_code: int + ) -> None: - base_params["name"] = name - with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): - with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): - response = await compute_client.post( - app.url_path_for("compute:create_docker_node", project_id=compute_project.id), json=base_params - ) - assert response.status_code == status_code + base_params["name"] = name + with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): + with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): + response = await compute_client.post( + app.url_path_for("compute:create_docker_node", project_id=compute_project.id), json=base_params + ) + assert response.status_code == status_code -async def test_docker_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_docker_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start", return_value=True) as mock: + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:start_docker_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_docker_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:stop_docker_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_docker_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.restart", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:reload_docker_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_docker_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.delete", return_value=True) as mock: - response = await compute_client.delete(app.url_path_for("compute:delete_docker_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_docker_pause(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:pause_docker_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_docker_unpause(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:unpause_docker_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_docker_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1"} - - url = app.url_path_for("compute:create_docker_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_docker_update_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for("compute:create_docker_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - - url = app.url_path_for("compute:update_docker_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_update_nio_binding"): - response = await compute_client.put(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - - -async def test_docker_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - url = app.url_path_for("compute:delete_docker_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_remove_nio_binding"): - response = await compute_client.delete(url) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_docker_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None: - - params = { - "name": "test", - "console": free_console_port, - "start_command": "yes", - "environment": "GNS3=1\nGNS4=0", - "extra_hosts": "test:127.0.0.1" - } - - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.update") as mock: - response = await compute_client.put(app.url_path_for("compute:update_docker_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json=params) - - assert response.status_code == 200 - assert mock.called - assert response.json()["name"] == "test" - assert response.json()["console"] == free_console_port - assert response.json()["start_command"] == "yes" - assert response.json()["environment"] == "GNS3=1\nGNS4=0" - assert response.json()["extra_hosts"] == "test:127.0.0.1" - - -async def test_docker_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - url = app.url_path_for("compute:start_docker_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start_capture") as mock: - params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_200_OK + response = await compute_client.post(app.url_path_for("compute:start_docker_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) assert mock.called - assert "test.pcap" in response.json()["pcap_file_path"] - - -async def test_docker_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - url = app.url_path_for("compute:stop_docker_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop_capture") as mock: - response = await compute_client.post(url) assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_docker_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:stop_docker_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT -async def test_docker_duplicate(app: FastAPI, compute_client: AsyncClient, vm: dict, base_params: dict) -> None: + async def test_docker_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - # create destination node first - with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): - with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): - response = await compute_client.post(app.url_path_for("compute:create_docker_node", - project_id=vm["project_id"]), json=base_params) - assert response.status_code == status.HTTP_201_CREATED + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.restart", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:reload_docker_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT - params = {"destination_node_id": response.json()["node_id"]} - response = await compute_client.post(app.url_path_for("compute:duplicate_docker_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json=params) - assert response.status_code == status.HTTP_201_CREATED + + async def test_docker_delete(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.delete", return_value=True) as mock: + response = await compute_client.delete(app.url_path_for("compute:delete_docker_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_docker_pause(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:pause_docker_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_docker_unpause(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:unpause_docker_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_docker_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1"} + + url = app.url_path_for("compute:create_docker_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + async def test_docker_update_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + url = app.url_path_for("compute:create_docker_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + + url = app.url_path_for("compute:update_docker_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_update_nio_binding"): + response = await compute_client.put(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + + + async def test_docker_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + url = app.url_path_for("compute:delete_docker_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_remove_nio_binding"): + response = await compute_client.delete(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_docker_update(self, app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None: + + params = { + "name": "test", + "console": free_console_port, + "start_command": "yes", + "environment": "GNS3=1\nGNS4=0", + "extra_hosts": "test:127.0.0.1" + } + + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.update") as mock: + response = await compute_client.put(app.url_path_for("compute:update_docker_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json=params) + + assert response.status_code == 200 + assert mock.called + assert response.json()["name"] == "test" + assert response.json()["console"] == free_console_port + assert response.json()["start_command"] == "yes" + assert response.json()["environment"] == "GNS3=1\nGNS4=0" + assert response.json()["extra_hosts"] == "test:127.0.0.1" + + + async def test_docker_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + url = app.url_path_for("compute:start_docker_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start_capture") as mock: + params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_200_OK + assert mock.called + assert "test.pcap" in response.json()["pcap_file_path"] + + + async def test_docker_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + url = app.url_path_for("compute:stop_docker_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop_capture") as mock: + response = await compute_client.post(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called + + + async def test_docker_duplicate(self, app: FastAPI, compute_client: AsyncClient, vm: dict, base_params: dict) -> None: + + # create destination node first + with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): + with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): + response = await compute_client.post(app.url_path_for("compute:create_docker_node", + project_id=vm["project_id"]), json=base_params) + assert response.status_code == status.HTTP_201_CREATED + + params = {"destination_node_id": response.json()["node_id"]} + response = await compute_client.post(app.url_path_for("compute:duplicate_docker_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json=params) + assert response.status_code == status.HTTP_201_CREATED diff --git a/tests/api/routes/compute/test_dynamips_nodes.py b/tests/api/routes/compute/test_dynamips_nodes.py index 19bc691c..840d2b3d 100644 --- a/tests/api/routes/compute/test_dynamips_nodes.py +++ b/tests/api/routes/compute/test_dynamips_nodes.py @@ -26,196 +26,208 @@ from httpx import AsyncClient pytestmark = pytest.mark.asyncio -# @pytest.yield_fixture(scope="module") -# async def vm(compute_api, compute_project, fake_image): -# -# dynamips_path = "/fake/dynamips" -# params = { -# "name": "My router", -# "platform": "c3745", -# "image": fake_image, -# "ram": 128 -# } -# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True) as mock: -# response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params) -# assert mock.called -# assert response.status == 201 -# -# #with asyncio_patch("gns3server.compute.dynamips.Dynamips.find_dynamips", return_value=dynamips_path): -# # yield response.json + +class TestDynamipsNodesRoutes: + + # @pytest.yield_fixture(scope="module") + # async def vm(compute_api, compute_project, fake_image): + # + # dynamips_path = "/fake/dynamips" + # params = { + # "name": "My router", + # "platform": "c3745", + # "image": fake_image, + # "ram": 128 + # } + # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True) as mock: + # response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params) + # assert mock.called + # assert response.status == 201 + # + # #with asyncio_patch("gns3server.compute.dynamips.Dynamips.find_dynamips", return_value=dynamips_path): + # # yield response.json -# async def test_dynamips_vm_create(compute_api, compute_project, fake_image): -# -# params = { -# "name": "My router", -# "platform": "c3745", -# "image": os.path.basename(fake_image), -# "ram": 128 -# } -# -# print(fake_image) -# -# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True): -# response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params) -# assert response.status == 201 -# assert response.json["name"] == "My router" -# assert response.json["project_id"] == compute_project.id -# assert response.json["dynamips_id"] + # async def test_dynamips_vm_create(compute_api, compute_project, fake_image): + # + # params = { + # "name": "My router", + # "platform": "c3745", + # "image": os.path.basename(fake_image), + # "ram": 128 + # } + # + # print(fake_image) + # + # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.create", return_value=True): + # response = await compute_api.post("/projects/{project_id}/dynamips/nodes".format(project_id=compute_project.id), params) + # assert response.status == 201 + # assert response.json["name"] == "My router" + # assert response.json["project_id"] == compute_project.id + # assert response.json["dynamips_id"] -# def test_dynamips_vm_get(compute_api, project, vm): -# response = compute_api.get("/projects/{project_id}/dynamips/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) -# assert response.status == 200 -# assert response.route == "/projects/{project_id}/dynamips/nodes/{node_id}" -# assert response.json["name"] == "My router" -# assert response.json["project_id"] == project.id -# -# -# def test_dynamips_vm_start(compute_api, vm): -# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.start", return_value=True) as mock: -# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"])) -# assert mock.called -# assert response.status == 204 -# -# -# def test_dynamips_vm_stop(compute_api, vm): -# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.stop", return_value=True) as mock: -# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) -# assert mock.called -# assert response.status == 204 -# -# -# def test_dynamips_vm_suspend(compute_api, vm): -# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.suspend", return_value=True) as mock: -# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"])) -# assert mock.called -# assert response.status == 204 -# -# -# def test_dynamips_vm_resume(compute_api, vm): -# with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.resume", return_value=True) as mock: -# response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"])) -# assert mock.called -# assert response.status == 204 + # def test_dynamips_vm_get(compute_api, project, vm): + # response = compute_api.get("/projects/{project_id}/dynamips/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) + # assert response.status == 200 + # assert response.route == "/projects/{project_id}/dynamips/nodes/{node_id}" + # assert response.json["name"] == "My router" + # assert response.json["project_id"] == project.id + # + # + # def test_dynamips_vm_start(compute_api, vm): + # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.start", return_value=True) as mock: + # response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"])) + # assert mock.called + # assert response.status == 204 + # + # + # def test_dynamips_vm_stop(compute_api, vm): + # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.stop", return_value=True) as mock: + # response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) + # assert mock.called + # assert response.status == 204 + # + # + # def test_dynamips_vm_suspend(compute_api, vm): + # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.suspend", return_value=True) as mock: + # response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"])) + # assert mock.called + # assert response.status == 204 + # + # + # def test_dynamips_vm_resume(compute_api, vm): + # with asyncio_patch("gns3server.compute.dynamips.nodes.router.Router.resume", return_value=True) as mock: + # response = compute_api.post("/projects/{project_id}/dynamips/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"])) + # assert mock.called + # assert response.status == 204 -# def test_vbox_nio_create_udp(compute_api, vm): -# -# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock: -# response = compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"], -# node_id=vm["node_id"]), {"type": "nio_udp", -# "lport": 4242, -# "rport": 4343, -# "rhost": "127.0.0.1"}, -# example=True) -# -# assert mock.called -# args, kwgars = mock.call_args -# assert args[0] == 0 -# -# assert response.status == 201 -# assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio" -# assert response.json["type"] == "nio_udp" -# -# -# def test_vbox_delete_nio(compute_api, vm): -# -# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: -# response = compute_api.delete("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) -# -# assert mock.called -# args, kwgars = mock.call_args -# assert args[0] == 0 -# -# assert response.status == 204 -# assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio" -# -# -# def test_vbox_update(compute_api, vm, free_console_port): -# response = compute_api.put("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test", -# "console": free_console_port}) -# assert response.status == 200 -# assert response.json["name"] == "test" -# assert response.json["console"] == free_console_port + # def test_vbox_nio_create_udp(compute_api, vm): + # + # with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock: + # response = compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"], + # node_id=vm["node_id"]), {"type": "nio_udp", + # "lport": 4242, + # "rport": 4343, + # "rhost": "127.0.0.1"}, + # example=True) + # + # assert mock.called + # args, kwgars = mock.call_args + # assert args[0] == 0 + # + # assert response.status == 201 + # assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio" + # assert response.json["type"] == "nio_udp" + # + # + # def test_vbox_delete_nio(compute_api, vm): + # + # with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: + # response = compute_api.delete("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) + # + # assert mock.called + # args, kwgars = mock.call_args + # assert args[0] == 0 + # + # assert response.status == 204 + # assert response.route == "/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/{adapter_id:\d+}/nio" + # + # + # def test_vbox_update(compute_api, vm, free_console_port): + # response = compute_api.put("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test", + # "console": free_console_port}) + # assert response.status == 200 + # assert response.json["name"] == "test" + # assert response.json["console"] == free_console_port -@pytest.fixture -def fake_image(tmpdir) -> str: - """Create a fake Dynamips image on disk""" + @pytest.fixture + def fake_image(self, tmpdir) -> str: + """Create a fake Dynamips image on disk""" - path = str(tmpdir / "7200.bin") - with open(path, "wb+") as f: - f.write(b'\x7fELF\x01\x02\x01') - os.chmod(path, stat.S_IREAD) - return path + path = str(tmpdir / "7200.bin") + with open(path, "wb+") as f: + f.write(b'\x7fELF\x01\x02\x01') + os.chmod(path, stat.S_IREAD) + return path -@pytest.fixture -def fake_file(tmpdir) -> str: - """Create a fake file disk""" + @pytest.fixture + def fake_file(self, tmpdir) -> str: + """Create a fake file disk""" - path = str(tmpdir / "7200.txt") - with open(path, "w+") as f: - f.write('1') - os.chmod(path, stat.S_IREAD) - return path + path = str(tmpdir / "7200.txt") + with open(path, "w+") as f: + f.write('1') + os.chmod(path, stat.S_IREAD) + return path -async def test_images(app: FastAPI, compute_client: AsyncClient, tmpdir, fake_image: str, fake_file: str) -> None: + async def test_images( + self, app: FastAPI, + compute_client: AsyncClient, + tmpdir, fake_image: str, + fake_file: str + ) -> None: - with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)): - response = await compute_client.get(app.url_path_for("compute:get_dynamips_images")) - assert response.status_code == status.HTTP_200_OK - assert response.json() == [{"filename": "7200.bin", - "path": "7200.bin", - "filesize": 7, - "md5sum": "b0d5aa897d937aced5a6b1046e8f7e2e"}] + with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)): + response = await compute_client.get(app.url_path_for("compute:get_dynamips_images")) + assert response.status_code == status.HTTP_200_OK + assert response.json() == [{"filename": "7200.bin", + "path": "7200.bin", + "filesize": 7, + "md5sum": "b0d5aa897d937aced5a6b1046e8f7e2e"}] -async def test_upload_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: + async def test_upload_image(self, app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: - response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test2"), content=b"TEST") - assert response.status_code == status.HTTP_204_NO_CONTENT + response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test2"), content=b"TEST") + assert response.status_code == status.HTTP_204_NO_CONTENT - with open(os.path.join(images_dir, "IOS", "test2")) as f: - assert f.read() == "TEST" + with open(os.path.join(images_dir, "IOS", "test2")) as f: + assert f.read() == "TEST" - with open(os.path.join(images_dir, "IOS", "test2.md5sum")) as f: - checksum = f.read() - assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" + with open(os.path.join(images_dir, "IOS", "test2.md5sum")) as f: + checksum = f.read() + assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" -async def test_upload_image_forbidden_location(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_upload_image_forbidden_location(self, app: FastAPI, compute_client: AsyncClient) -> None: - file_path = "%2e%2e/hello" - response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename=file_path), content=b"TEST") - assert response.status_code == status.HTTP_403_FORBIDDEN + file_path = "%2e%2e/hello" + response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename=file_path), content=b"TEST") + assert response.status_code == status.HTTP_403_FORBIDDEN -async def test_download_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: + async def test_download_image(self, app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: - response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test3"), content=b"TEST") - assert response.status_code == status.HTTP_204_NO_CONTENT + response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test3"), content=b"TEST") + assert response.status_code == status.HTTP_204_NO_CONTENT - response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename="test3")) - assert response.status_code == status.HTTP_200_OK + response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename="test3")) + assert response.status_code == status.HTTP_200_OK -async def test_download_image_forbidden(app: FastAPI, compute_client: AsyncClient, tmpdir) -> None: + async def test_download_image_forbidden(self, app: FastAPI, compute_client: AsyncClient, tmpdir) -> None: - file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" - response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename=file_path)) - assert response.status_code == status.HTTP_403_FORBIDDEN + file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" + response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename=file_path)) + assert response.status_code == status.HTTP_403_FORBIDDEN -@pytest.mark.skipif(os.getuid() == 0, reason="Root can delete any image") -async def test_upload_image_permission_denied(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: + @pytest.mark.skipif(os.getuid() == 0, reason="Root can delete any image") + async def test_upload_image_permission_denied( + self, app: FastAPI, + compute_client: AsyncClient, + images_dir: str + ) -> None: - os.makedirs(os.path.join(images_dir, "IOS"), exist_ok=True) - with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f: - f.write("") - os.chmod(os.path.join(images_dir, "IOS", "test2.tmp"), 0) + os.makedirs(os.path.join(images_dir, "IOS"), exist_ok=True) + with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f: + f.write("") + os.chmod(os.path.join(images_dir, "IOS", "test2.tmp"), 0) - response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test2"), content=b"TEST") - assert response.status_code == status.HTTP_409_CONFLICT + response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test2"), content=b"TEST") + assert response.status_code == status.HTTP_409_CONFLICT diff --git a/tests/api/routes/compute/test_ethernet_switch_nodes.py b/tests/api/routes/compute/test_ethernet_switch_nodes.py index 67390685..f0dc400b 100644 --- a/tests/api/routes/compute/test_ethernet_switch_nodes.py +++ b/tests/api/routes/compute/test_ethernet_switch_nodes.py @@ -28,427 +28,474 @@ from gns3server.compute.project import Project pytestmark = pytest.mark.asyncio -@pytest_asyncio.fixture -async def ethernet_switch(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> dict: +class TestEthernetSwitchNodesRoutes: - params = {"name": "Ethernet Switch"} - with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock: - response = await compute_client.post( - app.url_path_for("compute:create_ethernet_switch", project_id=compute_project.id), - json=params + @pytest_asyncio.fixture + async def ethernet_switch(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> dict: + + params = {"name": "Ethernet Switch"} + with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock: + response = await compute_client.post( + app.url_path_for("compute:create_ethernet_switch", project_id=compute_project.id), + json=params + ) + assert mock.called + assert response.status_code == status.HTTP_201_CREATED + + json_response = response.json() + node = compute_project.get_node(json_response["node_id"]) + node._hypervisor = AsyncioMagicMock() + node._hypervisor.send = AsyncioMagicMock() + node._hypervisor.version = "0.2.16" + return json_response + + + async def test_ethernet_switch_create( + self, app: FastAPI, + compute_client: AsyncClient, + compute_project: Project + ) -> None: + + params = {"name": "Ethernet Switch 1"} + with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock: + response = await compute_client.post( + app.url_path_for("compute:create_ethernet_switch", project_id=compute_project.id), + json=params + ) + assert mock.called + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "Ethernet Switch 1" + assert response.json()["project_id"] == compute_project.id + + + async def test_ethernet_switch_get( + self, app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + ethernet_switch: dict + ) -> None: + + response = await compute_client.get( + app.url_path_for( + "compute:get_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"] + ) ) - assert mock.called - assert response.status_code == status.HTTP_201_CREATED - - json_response = response.json() - node = compute_project.get_node(json_response["node_id"]) - node._hypervisor = AsyncioMagicMock() - node._hypervisor.send = AsyncioMagicMock() - node._hypervisor.version = "0.2.16" - return json_response - - -async def test_ethernet_switch_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: - - params = {"name": "Ethernet Switch 1"} - with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock: - response = await compute_client.post( - app.url_path_for("compute:create_ethernet_switch", project_id=compute_project.id), - json=params - ) - assert mock.called - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "Ethernet Switch 1" + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "Ethernet Switch" assert response.json()["project_id"] == compute_project.id + assert response.json()["status"] == "started" -async def test_ethernet_switch_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, ethernet_switch: dict) -> None: + async def test_ethernet_switch_duplicate( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + ethernet_switch: dict + ) -> None: - response = await compute_client.get( - app.url_path_for( - "compute:get_ethernet_switch", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"] - ) - ) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "Ethernet Switch" - assert response.json()["project_id"] == compute_project.id - assert response.json()["status"] == "started" + # create destination switch first + params = {"name": "Ethernet Switch 2"} + with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock: + response = await compute_client.post( + app.url_path_for( + "compute:create_ethernet_switch", + project_id=compute_project.id), + json=params + ) + assert mock.called + assert response.status_code == status.HTTP_201_CREATED - -async def test_ethernet_switch_duplicate( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - ethernet_switch: dict -) -> None: - - # create destination switch first - params = {"name": "Ethernet Switch 2"} - with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.create") as mock: + params = {"destination_node_id": response.json()["node_id"]} response = await compute_client.post( app.url_path_for( - "compute:create_ethernet_switch", - project_id=compute_project.id), - json=params + "compute:duplicate_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"]), json=params ) - assert mock.called assert response.status_code == status.HTTP_201_CREATED - params = {"destination_node_id": response.json()["node_id"]} - response = await compute_client.post( - app.url_path_for( - "compute:duplicate_ethernet_switch", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"]), json=params + + async def test_ethernet_switch_update( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + ethernet_switch: dict + ) -> None: + + params = { + "name": "test", + "console_type": "telnet" + } + + response = await compute_client.put( + app.url_path_for( + "compute:update_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"]), + json=params + ) + + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + node = compute_project.get_node(ethernet_switch["node_id"]) + node._hypervisor.send.assert_called_with("ethsw rename \"Ethernet Switch\" \"test\"") + + + async def test_ethernet_switch_update_ports( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + ethernet_switch: dict + ) -> None: + + port_params = { + "ports_mapping": [ + { + "name": "Ethernet0", + "port_number": 0, + "type": "qinq", + "vlan": 1 + }, + { + "name": "Ethernet1", + "port_number": 1, + "type": "qinq", + "vlan": 2, + "ethertype": "0x88A8" + }, + { + "name": "Ethernet2", + "port_number": 2, + "type": "dot1q", + "vlan": 3, + }, + { + "name": "Ethernet3", + "port_number": 3, + "type": "access", + "vlan": 4, + } + ], + } + + response = await compute_client.put( + app.url_path_for( + "compute:update_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"]), + json=port_params + ) + assert response.status_code == status.HTTP_200_OK + + nio_params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + for port_mapping in port_params["ports_mapping"]: + port_number = port_mapping["port_number"] + vlan = port_mapping["vlan"] + port_type = port_mapping["type"] + ethertype = port_mapping.get("ethertype", "") + url = app.url_path_for( + "compute:create_ethernet_switch_nio", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"], + adapter_number="0", + port_number=f"{port_number}" + ) + await compute_client.post(url, json=nio_params) + + node = compute_project.get_node(ethernet_switch["node_id"]) + nio = node.get_nio(port_number) + calls = [ + call.send(f'nio create_udp {nio.name} 4242 127.0.0.1 4343'), + call.send(f'ethsw add_nio "Ethernet Switch" {nio.name}'), + call.send(f'ethsw set_{port_type}_port "Ethernet Switch" {nio.name} {vlan} {ethertype}'.strip()) + ] + node._hypervisor.send.assert_has_calls(calls) + node._hypervisor.send.reset_mock() + + + @pytest.mark.parametrize( + "ports_settings", + ( + ( + { + "name": "Ethernet0", + "port_number": 0, + "type": "dot42q", # invalid port type + "vlan": 1, + } + ), + ( + { + "name": "Ethernet0", + "port_number": 0, + "type": "access", # missing vlan field + } + ), + ( + { + "name": "Ethernet0", + "port_number": 0, + "type": "dot1q", + "vlan": 1, + "ethertype": "0x88A8" # EtherType is only for QinQ + } + ), + ( + { + "name": "Ethernet0", + "port_number": 0, + "type": "qinq", + "vlan": 1, + "ethertype": "0x4242" # not a valid EtherType + } + ), + ( + { + "name": "Ethernet0", + "port_number": 0, + "type": "access", + "vlan": 0, # minimum vlan number is 1 + } + ), + ( + { + "name": "Ethernet0", + "port_number": 0, + "type": "access", + "vlan": 4242, # maximum vlan number is 4094 + } + ), + ) ) - assert response.status_code == status.HTTP_201_CREATED + async def test_ethernet_switch_update_ports_invalid( + self, + app: FastAPI, + compute_client: AsyncClient, + ethernet_switch: dict, + ports_settings: dict, + ) -> None: + + port_params = { + "ports_mapping": [ports_settings] + } + + response = await compute_client.put( + app.url_path_for( + "compute:update_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"]), + json=port_params + ) + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY -async def test_ethernet_switch_update( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - ethernet_switch: dict -) -> None: + async def test_ethernet_switch_delete( + self, app: FastAPI, + compute_client: AsyncClient, + ethernet_switch: dict + ) -> None: - params = { - "name": "test", - "console_type": "telnet" - } - - response = await compute_client.put( - app.url_path_for( - "compute:update_ethernet_switch", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"]), - json=params - ) - - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - node = compute_project.get_node(ethernet_switch["node_id"]) - node._hypervisor.send.assert_called_with("ethsw rename \"Ethernet Switch\" \"test\"") + response = await compute_client.delete( + app.url_path_for( + "compute:delete_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"] + ) + ) + assert response.status_code == status.HTTP_204_NO_CONTENT -async def test_ethernet_switch_update_ports( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - ethernet_switch: dict -) -> None: + async def test_ethernet_switch_start( + self, app: FastAPI, + compute_client: AsyncClient, + ethernet_switch: dict + ) -> None: - port_params = { - "ports_mapping": [ - { - "name": "Ethernet0", - "port_number": 0, - "type": "qinq", - "vlan": 1 - }, - { - "name": "Ethernet1", - "port_number": 1, - "type": "qinq", - "vlan": 2, - "ethertype": "0x88A8" - }, - { - "name": "Ethernet2", - "port_number": 2, - "type": "dot1q", - "vlan": 3, - }, - { - "name": "Ethernet3", - "port_number": 3, - "type": "access", - "vlan": 4, - } - ], - } + response = await compute_client.post( + app.url_path_for( + "compute:start_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"]) + ) + assert response.status_code == status.HTTP_204_NO_CONTENT - response = await compute_client.put( - app.url_path_for( - "compute:update_ethernet_switch", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"]), - json=port_params - ) - assert response.status_code == status.HTTP_200_OK - nio_params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } + async def test_ethernet_switch_stop( + self, app: FastAPI, + compute_client: AsyncClient, + ethernet_switch: dict + ) -> None: + + response = await compute_client.post( + app.url_path_for( + "compute:stop_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"]) + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_ethernet_switch_suspend( + self, app: FastAPI, + compute_client: AsyncClient, + ethernet_switch: dict + ) -> None: + + response = await compute_client.post( + app.url_path_for( + "compute:suspend_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"]) + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_ethernet_switch_reload( + self, app: FastAPI, + compute_client: AsyncClient, + ethernet_switch: dict + ) -> None: + + response = await compute_client.post( + app.url_path_for( + "compute:reload_ethernet_switch", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"]) + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_ethernet_switch_create_udp( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + ethernet_switch: dict + ) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } - for port_mapping in port_params["ports_mapping"]: - port_number = port_mapping["port_number"] - vlan = port_mapping["vlan"] - port_type = port_mapping["type"] - ethertype = port_mapping.get("ethertype", "") url = app.url_path_for( "compute:create_ethernet_switch_nio", project_id=ethernet_switch["project_id"], node_id=ethernet_switch["node_id"], adapter_number="0", - port_number=f"{port_number}" + port_number="0" ) - await compute_client.post(url, json=nio_params) + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" node = compute_project.get_node(ethernet_switch["node_id"]) - nio = node.get_nio(port_number) + nio = node.get_nio(0) calls = [ call.send(f'nio create_udp {nio.name} 4242 127.0.0.1 4343'), call.send(f'ethsw add_nio "Ethernet Switch" {nio.name}'), - call.send(f'ethsw set_{port_type}_port "Ethernet Switch" {nio.name} {vlan} {ethertype}'.strip()) + call.send(f'ethsw set_access_port "Ethernet Switch" {nio.name} 1') ] node._hypervisor.send.assert_has_calls(calls) - node._hypervisor.send.reset_mock() -@pytest.mark.parametrize( - "ports_settings", - ( - ( - { - "name": "Ethernet0", - "port_number": 0, - "type": "dot42q", # invalid port type - "vlan": 1, - } - ), - ( - { - "name": "Ethernet0", - "port_number": 0, - "type": "access", # missing vlan field - } - ), - ( - { - "name": "Ethernet0", - "port_number": 0, - "type": "dot1q", - "vlan": 1, - "ethertype": "0x88A8" # EtherType is only for QinQ - } - ), - ( - { - "name": "Ethernet0", - "port_number": 0, - "type": "qinq", - "vlan": 1, - "ethertype": "0x4242" # not a valid EtherType - } - ), - ( - { - "name": "Ethernet0", - "port_number": 0, - "type": "access", - "vlan": 0, # minimum vlan number is 1 - } - ), - ( - { - "name": "Ethernet0", - "port_number": 0, - "type": "access", - "vlan": 4242, # maximum vlan number is 4094 - } - ), - ) -) -async def test_ethernet_switch_update_ports_invalid( - app: FastAPI, - compute_client: AsyncClient, - ethernet_switch: dict, - ports_settings: dict, -) -> None: + async def test_ethernet_switch_delete_nio( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + ethernet_switch: dict + ) -> None: - port_params = { - "ports_mapping": [ports_settings] - } + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } - response = await compute_client.put( - app.url_path_for( - "compute:update_ethernet_switch", + url = app.url_path_for( + "compute:create_ethernet_switch_nio", project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"]), - json=port_params - ) - assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - - -async def test_ethernet_switch_delete(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None: - - response = await compute_client.delete( - app.url_path_for( - "compute:delete_ethernet_switch", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"] + node_id=ethernet_switch["node_id"], + adapter_number="0", + port_number="0" ) - ) - assert response.status_code == status.HTTP_204_NO_CONTENT + await compute_client.post(url, json=params) + node = compute_project.get_node(ethernet_switch["node_id"]) + node._hypervisor.send.reset_mock() + nio = node.get_nio(0) -async def test_ethernet_switch_start(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None: - - response = await compute_client.post( - app.url_path_for( - "compute:start_ethernet_switch", + url = app.url_path_for( + "compute:delete_ethernet_switch_nio", project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"]) - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_ethernet_switch_stop(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None: - - response = await compute_client.post( - app.url_path_for( - "compute:stop_ethernet_switch", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"]) - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_ethernet_switch_suspend(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None: - - response = await compute_client.post( - app.url_path_for( - "compute:suspend_ethernet_switch", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"]) - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_ethernet_switch_reload(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None: - - response = await compute_client.post( - app.url_path_for( - "compute:reload_ethernet_switch", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"]) - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_ethernet_switch_create_udp( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - ethernet_switch: dict -) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for( - "compute:create_ethernet_switch_nio", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"], - adapter_number="0", - port_number="0" - ) - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - node = compute_project.get_node(ethernet_switch["node_id"]) - nio = node.get_nio(0) - calls = [ - call.send(f'nio create_udp {nio.name} 4242 127.0.0.1 4343'), - call.send(f'ethsw add_nio "Ethernet Switch" {nio.name}'), - call.send(f'ethsw set_access_port "Ethernet Switch" {nio.name} 1') - ] - node._hypervisor.send.assert_has_calls(calls) - - -async def test_ethernet_switch_delete_nio( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - ethernet_switch: dict -) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for( - "compute:create_ethernet_switch_nio", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"], - adapter_number="0", - port_number="0" - ) - await compute_client.post(url, json=params) - - node = compute_project.get_node(ethernet_switch["node_id"]) - node._hypervisor.send.reset_mock() - nio = node.get_nio(0) - - url = app.url_path_for( - "compute:delete_ethernet_switch_nio", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"], - adapter_number="0", - port_number="0" - ) - response = await compute_client.delete(url) - assert response.status_code == status.HTTP_204_NO_CONTENT - - calls = [ - call(f'ethsw remove_nio "Ethernet Switch" {nio.name}'), - call(f'nio delete {nio.name}') - ] - node._hypervisor.send.assert_has_calls(calls) - - -async def test_ethernet_switch_start_capture(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None: - - params = { - "capture_file_name": "test.pcap", - "data_link_type": "DLT_EN10MB" - } - - url = app.url_path_for("compute:start_ethernet_switch_capture", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"], - adapter_number="0", - port_number="0") - - with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.start_capture") as mock: - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_200_OK - assert mock.called - assert "test.pcap" in response.json()["pcap_file_path"] - - -async def test_ethernet_switch_stop_capture(app: FastAPI, compute_client: AsyncClient, ethernet_switch: dict) -> None: - - url = app.url_path_for("compute:stop_ethernet_switch_capture", - project_id=ethernet_switch["project_id"], - node_id=ethernet_switch["node_id"], - adapter_number="0", - port_number="0") - - with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.stop_capture") as mock: - response = await compute_client.post(url) + node_id=ethernet_switch["node_id"], + adapter_number="0", + port_number="0" + ) + response = await compute_client.delete(url) assert response.status_code == status.HTTP_204_NO_CONTENT - assert mock.called + + calls = [ + call(f'ethsw remove_nio "Ethernet Switch" {nio.name}'), + call(f'nio delete {nio.name}') + ] + node._hypervisor.send.assert_has_calls(calls) + + + async def test_ethernet_switch_start_capture( + self, + app: FastAPI, + compute_client: AsyncClient, + ethernet_switch: dict + ) -> None: + + params = { + "capture_file_name": "test.pcap", + "data_link_type": "DLT_EN10MB" + } + + url = app.url_path_for("compute:start_ethernet_switch_capture", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"], + adapter_number="0", + port_number="0") + + with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.start_capture") as mock: + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_200_OK + assert mock.called + assert "test.pcap" in response.json()["pcap_file_path"] + + + async def test_ethernet_switch_stop_capture( + self, + app: FastAPI, + compute_client: AsyncClient, + ethernet_switch: dict + ) -> None: + + url = app.url_path_for("compute:stop_ethernet_switch_capture", + project_id=ethernet_switch["project_id"], + node_id=ethernet_switch["node_id"], + adapter_number="0", + port_number="0") + + with asyncio_patch("gns3server.compute.dynamips.nodes.ethernet_switch.EthernetSwitch.stop_capture") as mock: + response = await compute_client.post(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called diff --git a/tests/api/routes/compute/test_iou_nodes.py b/tests/api/routes/compute/test_iou_nodes.py index d894ac30..8343ba33 100644 --- a/tests/api/routes/compute/test_iou_nodes.py +++ b/tests/api/routes/compute/test_iou_nodes.py @@ -28,452 +28,502 @@ from unittest.mock import patch from gns3server.compute.project import Project -pytestmark = [pytest.mark.asyncio] +pytestmark = pytest.mark.asyncio -@pytest.fixture -def fake_iou_bin(images_dir) -> str: - """Create a fake IOU image on disk""" +class TestIOUNodesRoutes: - path = os.path.join(images_dir, "IOU", "iou.bin") - with open(path, "w+") as f: - f.write('\x7fELF\x01\x01\x01') - os.chmod(path, stat.S_IREAD | stat.S_IEXEC) - return path + @pytest.fixture + def fake_iou_bin(self, images_dir) -> str: + """Create a fake IOU image on disk""" + + path = os.path.join(images_dir, "IOU", "iou.bin") + with open(path, "w+") as f: + f.write('\x7fELF\x01\x01\x01') + os.chmod(path, stat.S_IREAD | stat.S_IEXEC) + return path -@pytest.fixture -def base_params(tmpdir, fake_iou_bin) -> dict: - """Return standard parameters""" + @pytest.fixture + def base_params(self, tmpdir, fake_iou_bin) -> dict: + """Return standard parameters""" - return {"application_id": 42, "name": "IOU-TEST-1", "path": "iou.bin"} + return {"application_id": 42, "name": "IOU-TEST-1", "path": "iou.bin"} -@pytest_asyncio.fixture -async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> dict: + @pytest_asyncio.fixture + async def vm( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict + ) -> dict: - response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params) - assert response.status_code == status.HTTP_201_CREATED - return response.json() + response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params) + assert response.status_code == status.HTTP_201_CREATED + return response.json() -def startup_config_file(compute_project: Project, vm: dict) -> str: + def startup_config_file(self, compute_project: Project, vm: dict) -> str: - directory = os.path.join(compute_project.path, "project-files", "iou", vm["node_id"]) - os.makedirs(directory, exist_ok=True) - return os.path.join(directory, "startup-config.cfg") + directory = os.path.join(compute_project.path, "project-files", "iou", vm["node_id"]) + os.makedirs(directory, exist_ok=True) + return os.path.join(directory, "startup-config.cfg") -async def test_iou_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> None: + async def test_iou_create( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict + ) -> None: - response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "IOU-TEST-1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["serial_adapters"] == 2 - assert response.json()["ethernet_adapters"] == 2 - assert response.json()["ram"] == 256 - assert response.json()["nvram"] == 128 - assert response.json()["l1_keepalives"] is False + response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "IOU-TEST-1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["serial_adapters"] == 2 + assert response.json()["ethernet_adapters"] == 2 + assert response.json()["ram"] == 256 + assert response.json()["nvram"] == 128 + assert response.json()["l1_keepalives"] is False -async def test_iou_create_with_params(app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - base_params: dict) -> None: + async def test_iou_create_with_params( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict + ) -> None: - params = base_params - params["ram"] = 1024 - params["nvram"] = 512 - params["serial_adapters"] = 4 - params["ethernet_adapters"] = 0 - params["l1_keepalives"] = True - params["startup_config_content"] = "hostname test" - params["use_default_iou_values"] = False + params = base_params + params["ram"] = 1024 + params["nvram"] = 512 + params["serial_adapters"] = 4 + params["ethernet_adapters"] = 0 + params["l1_keepalives"] = True + params["startup_config_content"] = "hostname test" + params["use_default_iou_values"] = False - response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "IOU-TEST-1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["serial_adapters"] == 4 - assert response.json()["ethernet_adapters"] == 0 - assert response.json()["ram"] == 1024 - assert response.json()["nvram"] == 512 - assert response.json()["l1_keepalives"] is True - assert response.json()["use_default_iou_values"] is False + response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "IOU-TEST-1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["serial_adapters"] == 4 + assert response.json()["ethernet_adapters"] == 0 + assert response.json()["ram"] == 1024 + assert response.json()["nvram"] == 512 + assert response.json()["l1_keepalives"] is True + assert response.json()["use_default_iou_values"] is False - with open(startup_config_file(compute_project, response.json())) as f: - assert f.read() == "hostname test" + with open(self.startup_config_file(compute_project, response.json())) as f: + assert f.read() == "hostname test" -@pytest.mark.parametrize( - "name, status_code", - ( - ("valid-name", status.HTTP_201_CREATED), - ("42name", status.HTTP_409_CONFLICT), - ("name42", status.HTTP_201_CREATED), - ("-name", status.HTTP_409_CONFLICT), - ("name%-test", status.HTTP_409_CONFLICT), - ("x" * 63, status.HTTP_201_CREATED), - ("x" * 64, status.HTTP_409_CONFLICT), - ), -) -async def test_iou_create_with_invalid_name( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - base_params: dict, - name: str, - status_code: int -) -> None: - - base_params["name"] = name - response = await compute_client.post( - app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params + @pytest.mark.parametrize( + "name, status_code", + ( + ("valid-name", status.HTTP_201_CREATED), + ("42name", status.HTTP_409_CONFLICT), + ("name42", status.HTTP_201_CREATED), + ("-name", status.HTTP_409_CONFLICT), + ("name%-test", status.HTTP_409_CONFLICT), + ("x" * 63, status.HTTP_201_CREATED), + ("x" * 64, status.HTTP_409_CONFLICT), + ), ) - assert response.status_code == status_code + async def test_iou_create_with_invalid_name( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict, + name: str, + status_code: int + ) -> None: + + base_params["name"] = name + response = await compute_client.post( + app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=base_params + ) + assert response.status_code == status_code -async def test_iou_create_startup_config_already_exist( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - base_params: dict) -> None: - """We don't erase a startup-config if already exist at project creation""" + async def test_iou_create_startup_config_already_exist( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict) -> None: + """We don't erase a startup-config if already exist at project creation""" - node_id = str(uuid.uuid4()) - startup_config_file_path = startup_config_file(compute_project, {'node_id': node_id}) - with open(startup_config_file_path, 'w+') as f: - f.write("echo hello") + node_id = str(uuid.uuid4()) + startup_config_file_path = self.startup_config_file(compute_project, {'node_id': node_id}) + with open(startup_config_file_path, 'w+') as f: + f.write("echo hello") - params = base_params - params["node_id"] = node_id - params["startup_config_content"] = "hostname test" + params = base_params + params["node_id"] = node_id + params["startup_config_content"] = "hostname test" - response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED + response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=compute_project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED - with open(startup_config_file(compute_project, response.json())) as f: - assert f.read() == "echo hello" + with open(self.startup_config_file(compute_project, response.json())) as f: + assert f.read() == "echo hello" -async def test_iou_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: + async def test_iou_get( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: - response = await compute_client.get(app.url_path_for("compute:get_iou_node", project_id=vm["project_id"], node_id=vm["node_id"])) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "IOU-TEST-1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["serial_adapters"] == 2 - assert response.json()["ethernet_adapters"] == 2 - assert response.json()["ram"] == 256 - assert response.json()["nvram"] == 128 - assert response.json()["l1_keepalives"] is False + response = await compute_client.get(app.url_path_for("compute:get_iou_node", project_id=vm["project_id"], node_id=vm["node_id"])) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "IOU-TEST-1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["serial_adapters"] == 2 + assert response.json()["ethernet_adapters"] == 2 + assert response.json()["ram"] == 256 + assert response.json()["nvram"] == 128 + assert response.json()["l1_keepalives"] is False -async def test_iou_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_iou_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:start_iou_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json={}) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT + with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:start_iou_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json={}) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT -async def test_iou_start_with_iourc(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_iou_start_with_iourc(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - params = {"iourc_content": "test"} - with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:start_iou_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json=params) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT + params = {"iourc_content": "test"} + with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:start_iou_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json=params) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT -async def test_iou_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_iou_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:stop_iou_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT + with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:stop_iou_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT -async def test_iou_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_iou_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.reload", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:reload_iou_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT + with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.reload", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:reload_iou_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT -async def test_iou_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_iou_delete(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - with asyncio_patch("gns3server.compute.iou.IOU.delete_node", return_value=True) as mock: - response = await compute_client.delete(app.url_path_for("compute:delete_iou_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT + with asyncio_patch("gns3server.compute.iou.IOU.delete_node", return_value=True) as mock: + response = await compute_client.delete(app.url_path_for("compute:delete_iou_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT -async def test_iou_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None: + async def test_iou_update( + self, app: FastAPI, + compute_client: AsyncClient, + vm: dict, + free_console_port: int + ) -> None: - params = { - "name": "test", - "console": free_console_port, - "ram": 512, - "nvram": 2048, - "ethernet_adapters": 4, - "serial_adapters": 0, - "l1_keepalives": True, - "use_default_iou_values": True, - } + params = { + "name": "test", + "console": free_console_port, + "ram": 512, + "nvram": 2048, + "ethernet_adapters": 4, + "serial_adapters": 0, + "l1_keepalives": True, + "use_default_iou_values": True, + } - response = await compute_client.put(app.url_path_for("compute:update_iou_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json=params) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - assert response.json()["console"] == free_console_port - assert response.json()["ethernet_adapters"] == 4 - assert response.json()["serial_adapters"] == 0 - assert response.json()["ram"] == 512 - assert response.json()["nvram"] == 2048 - assert response.json()["l1_keepalives"] is True - assert response.json()["use_default_iou_values"] is True + response = await compute_client.put(app.url_path_for("compute:update_iou_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json=params) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + assert response.json()["console"] == free_console_port + assert response.json()["ethernet_adapters"] == 4 + assert response.json()["serial_adapters"] == 0 + assert response.json()["ram"] == 512 + assert response.json()["nvram"] == 2048 + assert response.json()["l1_keepalives"] is True + assert response.json()["use_default_iou_values"] is True -async def test_iou_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_iou_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - params = {"type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1"} + params = {"type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1"} - url = app.url_path_for("compute:create_iou_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="1", - port_number="0") - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_iou_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = {"type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1"} - - url = app.url_path_for("compute:create_iou_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="1", - port_number="0") - - await compute_client.post(url, json=params) - params["filters"] = {} - - url = app.url_path_for("compute:update_iou_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="1", - port_number="0") - response = await compute_client.put(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_iou_nio_create_ethernet(app: FastAPI, compute_client: AsyncClient, vm: dict, ethernet_device: str) -> None: - - params = { - "type": "nio_ethernet", - "ethernet_device": ethernet_device - } - - url = app.url_path_for("compute:create_iou_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="1", - port_number="0") - - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_ethernet" - assert response.json()["ethernet_device"] == ethernet_device - - -async def test_iou_nio_create_ethernet_different_port(app: FastAPI, - compute_client: AsyncClient, - vm: dict, - ethernet_device: str) -> None: - - params = { - "type": "nio_ethernet", - "ethernet_device": ethernet_device - } - - url = app.url_path_for("compute:create_iou_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="3") - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_ethernet" - assert response.json()["ethernet_device"] == ethernet_device - - -async def test_iou_nio_create_tap(app: FastAPI, compute_client: AsyncClient, vm: dict, ethernet_device: str) -> None: - - params = { - "type": "nio_tap", - "tap_device": ethernet_device - } - - url = app.url_path_for("compute:create_iou_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="1", - port_number="0") - with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True): + url = app.url_path_for("compute:create_iou_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="1", + port_number="0") response = await compute_client.post(url, json=params) assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_tap" + assert response.json()["type"] == "nio_udp" -async def test_iou_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_iou_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } + params = {"type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1"} - url = app.url_path_for("compute:create_iou_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="1", - port_number="0") + url = app.url_path_for("compute:create_iou_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="1", + port_number="0") - await compute_client.post(url, json=params) + await compute_client.post(url, json=params) + params["filters"] = {} - url = app.url_path_for("compute:delete_iou_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="1", - port_number="0") - - response = await compute_client.delete(url) - assert response.status_code == status.HTTP_204_NO_CONTENT + url = app.url_path_for("compute:update_iou_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="1", + port_number="0") + response = await compute_client.put(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" -async def test_iou_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_iou_nio_create_ethernet( + self, + app: FastAPI, + compute_client: AsyncClient, + vm: dict, + ethernet_device: str + ) -> None: - params = { - "capture_file_name": "test.pcap", - "data_link_type": "DLT_EN10MB" - } + params = { + "type": "nio_ethernet", + "ethernet_device": ethernet_device + } - url = app.url_path_for("compute:start_iou_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") + url = app.url_path_for("compute:create_iou_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="1", + port_number="0") - with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start_capture") as mock: + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_ethernet" + assert response.json()["ethernet_device"] == ethernet_device + + + async def test_iou_nio_create_ethernet_different_port( + self, + app: FastAPI, + compute_client: AsyncClient, + vm: dict, + ethernet_device: str + ) -> None: + + params = { + "type": "nio_ethernet", + "ethernet_device": ethernet_device + } + + url = app.url_path_for("compute:create_iou_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="3") + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_ethernet" + assert response.json()["ethernet_device"] == ethernet_device + + + async def test_iou_nio_create_tap( + self, + app: FastAPI, + compute_client: AsyncClient, + vm: dict, + ethernet_device: str + ) -> None: + + params = { + "type": "nio_tap", + "tap_device": ethernet_device + } + + url = app.url_path_for("compute:create_iou_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="1", + port_number="0") + with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True): response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_200_OK - assert mock.called - assert "test.pcap" in response.json()["pcap_file_path"] + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_tap" -async def test_iou_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_iou_delete_nio( + self, + app: FastAPI, + compute_client: AsyncClient, + vm: dict + ) -> None: - url = app.url_path_for("compute:stop_iou_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } - with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop_capture") as mock: - response = await compute_client.post(url) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert mock.called + url = app.url_path_for("compute:create_iou_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="1", + port_number="0") + await compute_client.post(url, json=params) -# @pytest.mark.asyncio -# async def test_iou_pcap(compute_api, vm, compute_project): -# -# with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.get_nio"): -# with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"): -# response = await compute_client.get("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) -# assert response.status_code == status.HTTP_200_OK + url = app.url_path_for("compute:delete_iou_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="1", + port_number="0") - -async def test_images(app: FastAPI, compute_client: AsyncClient, fake_iou_bin: str) -> None: - - response = await compute_client.get(app.url_path_for("compute:get_iou_images")) - assert response.status_code == status.HTTP_200_OK - assert response.json() == [{"filename": "iou.bin", "path": "iou.bin", "filesize": 7, "md5sum": "e573e8f5c93c6c00783f20c7a170aa6c"}] - - -async def test_upload_image(app: FastAPI, compute_client: AsyncClient, tmpdir) -> None: - - with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir)): - response = await compute_client.post(app.url_path_for("compute:upload_iou_image", filename="test2"), content=b"TEST") + response = await compute_client.delete(url) assert response.status_code == status.HTTP_204_NO_CONTENT - with open(str(tmpdir / "test2")) as f: - assert f.read() == "TEST" - with open(str(tmpdir / "test2.md5sum")) as f: - checksum = f.read() - assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" + async def test_iou_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "capture_file_name": "test.pcap", + "data_link_type": "DLT_EN10MB" + } + + url = app.url_path_for("compute:start_iou_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start_capture") as mock: + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_200_OK + assert mock.called + assert "test.pcap" in response.json()["pcap_file_path"] -async def test_upload_image_forbidden_location(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_iou_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - file_path = "%2e%2e/hello" - response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename=file_path), content=b"TEST") - assert response.status_code == status.HTTP_403_FORBIDDEN + url = app.url_path_for("compute:stop_iou_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop_capture") as mock: + response = await compute_client.post(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called -async def test_download_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: - - response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test3"), content=b"TEST") - assert response.status_code == status.HTTP_204_NO_CONTENT - - response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename="test3")) - assert response.status_code == status.HTTP_200_OK + # @pytest.mark.asyncio + # async def test_iou_pcap(compute_api, vm, compute_project): + # + # with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.get_nio"): + # with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"): + # response = await compute_client.get("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) + # assert response.status_code == status.HTTP_200_OK -async def test_download_image_forbidden(app: FastAPI, compute_client: AsyncClient, tmpdir) -> None: + async def test_images(self, app: FastAPI, compute_client: AsyncClient, fake_iou_bin: str) -> None: - file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" - response = await compute_client.get(app.url_path_for("compute:download_iou_image", filename=file_path)) - assert response.status_code == status.HTTP_403_FORBIDDEN + response = await compute_client.get(app.url_path_for("compute:get_iou_images")) + assert response.status_code == status.HTTP_200_OK + assert response.json() == [{"filename": "iou.bin", "path": "iou.bin", "filesize": 7, "md5sum": "e573e8f5c93c6c00783f20c7a170aa6c"}] -async def test_iou_duplicate(app: FastAPI, compute_client: AsyncClient, vm: dict, base_params: dict) -> None: + async def test_upload_image(self, app: FastAPI, compute_client: AsyncClient, tmpdir) -> None: - # create destination node first - response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=vm["project_id"]), json=base_params) - assert response.status_code == status.HTTP_201_CREATED + with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir)): + response = await compute_client.post(app.url_path_for("compute:upload_iou_image", filename="test2"), content=b"TEST") + assert response.status_code == status.HTTP_204_NO_CONTENT - params = {"destination_node_id": response.json()["node_id"]} + with open(str(tmpdir / "test2")) as f: + assert f.read() == "TEST" - response = await compute_client.post(app.url_path_for("compute:duplicate_iou_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json=params) - assert response.status_code == status.HTTP_201_CREATED + with open(str(tmpdir / "test2.md5sum")) as f: + checksum = f.read() + assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" + + + async def test_upload_image_forbidden_location(self, app: FastAPI, compute_client: AsyncClient) -> None: + + file_path = "%2e%2e/hello" + response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename=file_path), content=b"TEST") + assert response.status_code == status.HTTP_403_FORBIDDEN + + + async def test_download_image(self, app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: + + response = await compute_client.post(app.url_path_for("compute:upload_dynamips_image", filename="test3"), content=b"TEST") + assert response.status_code == status.HTTP_204_NO_CONTENT + + response = await compute_client.get(app.url_path_for("compute:download_dynamips_image", filename="test3")) + assert response.status_code == status.HTTP_200_OK + + + async def test_download_image_forbidden(self, app: FastAPI, compute_client: AsyncClient, tmpdir) -> None: + + file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" + response = await compute_client.get(app.url_path_for("compute:download_iou_image", filename=file_path)) + assert response.status_code == status.HTTP_403_FORBIDDEN + + + async def test_iou_duplicate(self, app: FastAPI, compute_client: AsyncClient, vm: dict, base_params: dict) -> None: + + # create destination node first + response = await compute_client.post(app.url_path_for("compute:create_iou_node", project_id=vm["project_id"]), json=base_params) + assert response.status_code == status.HTTP_201_CREATED + + params = {"destination_node_id": response.json()["node_id"]} + + response = await compute_client.post(app.url_path_for("compute:duplicate_iou_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json=params) + assert response.status_code == status.HTTP_201_CREATED diff --git a/tests/api/routes/compute/test_nat_nodes.py b/tests/api/routes/compute/test_nat_nodes.py index 0f8b0d69..636a538a 100644 --- a/tests/api/routes/compute/test_nat_nodes.py +++ b/tests/api/routes/compute/test_nat_nodes.py @@ -27,167 +27,188 @@ from gns3server.compute.project import Project pytestmark = pytest.mark.asyncio -@pytest_asyncio.fixture(scope="function") -async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, ubridge_path: str, on_gns3vm) -> dict: +class TestNATNodesRoutes: - with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"): - response = await compute_client.post(app.url_path_for("compute:create_nat_node", project_id=compute_project.id), - json={"name": "Nat 1"}) - assert response.status_code == status.HTTP_201_CREATED - return response.json() + @pytest_asyncio.fixture + async def vm( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + ubridge_path: str, + on_gns3vm + ) -> dict: + + with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"): + response = await compute_client.post(app.url_path_for("compute:create_nat_node", project_id=compute_project.id), + json={"name": "Nat 1"}) + assert response.status_code == status.HTTP_201_CREATED + return response.json() -async def test_nat_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, on_gns3vm) -> None: + async def test_nat_create( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + on_gns3vm + ) -> None: - with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"): - response = await compute_client.post(app.url_path_for("compute:create_nat_node", project_id=compute_project.id), - json={"name": "Nat 1"}) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "Nat 1" - assert response.json()["project_id"] == compute_project.id + with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"): + response = await compute_client.post(app.url_path_for("compute:create_nat_node", project_id=compute_project.id), + json={"name": "Nat 1"}) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "Nat 1" + assert response.json()["project_id"] == compute_project.id -async def test_nat_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: + async def test_nat_get( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: - response = await compute_client.get(app.url_path_for("compute:get_nat_node", project_id=vm["project_id"], node_id=vm["node_id"])) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "Nat 1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["status"] == "started" - - -async def test_nat_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for("compute:create_nat_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_nat_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for("compute:create_nat_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - await compute_client.post(url, json=params) - params["filters"] = {} - - url = app.url_path_for("compute:update_nat_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - response = await compute_client.put(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_nat_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for("compute:create_nat_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): - await compute_client.post(url, json=params) - - url = app.url_path_for("compute:delete_nat_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.remove_nio") as mock: - response = await compute_client.delete(url) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_nat_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - response = await compute_client.delete(app.url_path_for("compute:delete_nat_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_nat_update(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - response = await compute_client.put(app.url_path_for("compute:update_nat_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json={"name": "test"}) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - - -async def test_nat_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "capture_file_name": "test.pcap", - "data_link_type": "DLT_EN10MB" - } - - url = app.url_path_for("compute:start_nat_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.start_capture") as mock: - response = await compute_client.post(url, json=params) + response = await compute_client.get(app.url_path_for("compute:get_nat_node", project_id=vm["project_id"], node_id=vm["node_id"])) assert response.status_code == status.HTTP_200_OK - assert mock.called - assert "test.pcap" in response.json()["pcap_file_path"] + assert response.json()["name"] == "Nat 1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["status"] == "started" -async def test_nat_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + async def test_nat_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - url = app.url_path_for("compute:stop_nat_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } - with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.stop_capture") as mock: - response = await compute_client.post(url) + url = app.url_path_for("compute:create_nat_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + async def test_nat_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + url = app.url_path_for("compute:create_nat_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + await compute_client.post(url, json=params) + params["filters"] = {} + + url = app.url_path_for("compute:update_nat_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + response = await compute_client.put(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + async def test_nat_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + url = app.url_path_for("compute:create_nat_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): + await compute_client.post(url, json=params) + + url = app.url_path_for("compute:delete_nat_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.remove_nio") as mock: + response = await compute_client.delete(url) + assert mock.called assert response.status_code == status.HTTP_204_NO_CONTENT - assert mock.called -# @pytest.mark.asyncio -# async def test_nat_pcap(compute_api, vm, compute_project): -# -# with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.get_nio"): -# with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"): -# response = await compute_client.get("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) -# assert response.status_code == status.HTTP_200_OK + async def test_nat_delete(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + response = await compute_client.delete(app.url_path_for("compute:delete_nat_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_nat_update(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + response = await compute_client.put(app.url_path_for("compute:update_nat_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json={"name": "test"}) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + + + async def test_nat_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "capture_file_name": "test.pcap", + "data_link_type": "DLT_EN10MB" + } + + url = app.url_path_for("compute:start_nat_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.start_capture") as mock: + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_200_OK + assert mock.called + assert "test.pcap" in response.json()["pcap_file_path"] + + + async def test_nat_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + url = app.url_path_for("compute:stop_nat_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.stop_capture") as mock: + response = await compute_client.post(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called + + + # @pytest.mark.asyncio + # async def test_nat_pcap(self, compute_api, vm, compute_project): + # + # with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.get_nio"): + # with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"): + # response = await compute_client.get("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) + # assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/compute/test_projects.py b/tests/api/routes/compute/test_projects.py index 669db760..27474044 100644 --- a/tests/api/routes/compute/test_projects.py +++ b/tests/api/routes/compute/test_projects.py @@ -30,173 +30,197 @@ from gns3server.compute.project import Project pytestmark = pytest.mark.asyncio -@pytest.fixture -def base_params(tmpdir) -> dict: - """Return standard parameters""" +class TestComputeProjectRoutes: - params = { - "name": "test", - "project_id": str(uuid.uuid4()) - } - return params + @pytest.fixture + def base_params(self, tmpdir) -> dict: + """Return standard parameters""" + + params = { + "name": "test", + "project_id": str(uuid.uuid4()) + } + return params -async def test_create_project_without_dir(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None: + async def test_create_project_without_dir( + self, + app: FastAPI, + compute_client: AsyncClient, + base_params: dict + ) -> None: - response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["project_id"] == base_params["project_id"] - assert response.json()["name"] == base_params["name"] + response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["project_id"] == base_params["project_id"] + assert response.json()["name"] == base_params["name"] -async def test_show_project(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None: + async def test_show_project( + self, + app: FastAPI, + compute_client: AsyncClient, + base_params: dict + ) -> None: - response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params) - assert response.status_code == status.HTTP_201_CREATED - response = await compute_client.get(app.url_path_for("compute:get_compute_project", project_id=base_params["project_id"])) + response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params) + assert response.status_code == status.HTTP_201_CREATED + response = await compute_client.get(app.url_path_for("compute:get_compute_project", project_id=base_params["project_id"])) - #print(response.json().keys()) - #assert len(response.json().keys()) == 3 - assert response.json()["project_id"] == base_params["project_id"] - assert response.json()["name"] == base_params["name"] - assert response.json()["variables"] is None + #print(response.json().keys()) + #assert len(response.json().keys()) == 3 + assert response.json()["project_id"] == base_params["project_id"] + assert response.json()["name"] == base_params["name"] + assert response.json()["variables"] is None -async def test_show_project_invalid_uuid(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_show_project_invalid_uuid(self, app: FastAPI, compute_client: AsyncClient) -> None: - response = await compute_client.get(app.url_path_for("compute:get_compute_project", - project_id="50010203-0405-0607-0809-0a0b0c0d0e42")) - assert response.status_code == status.HTTP_404_NOT_FOUND + response = await compute_client.get(app.url_path_for("compute:get_compute_project", + project_id="50010203-0405-0607-0809-0a0b0c0d0e42")) + assert response.status_code == status.HTTP_404_NOT_FOUND -async def test_list_projects(app: FastAPI, compute_client: AsyncClient) -> dict: + async def test_list_projects(self, app: FastAPI, compute_client: AsyncClient) -> dict: - ProjectManager.instance()._projects = {} + ProjectManager.instance()._projects = {} - params = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"} - response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=params) - assert response.status_code == status.HTTP_201_CREATED - params = {"name": "test", "project_id": "52010203-0405-0607-0809-0a0b0c0d0e0b"} - response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=params) - assert response.status_code == status.HTTP_201_CREATED + params = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"} + response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=params) + assert response.status_code == status.HTTP_201_CREATED + params = {"name": "test", "project_id": "52010203-0405-0607-0809-0a0b0c0d0e0b"} + response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=params) + assert response.status_code == status.HTTP_201_CREATED - response = await compute_client.get(app.url_path_for("compute:get_compute_projects")) - assert response.status_code == status.HTTP_200_OK - assert len(response.json()) == 2 - assert "51010203-0405-0607-0809-0a0b0c0d0e0f" in [p["project_id"] for p in response.json()] + response = await compute_client.get(app.url_path_for("compute:get_compute_projects")) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()) == 2 + assert "51010203-0405-0607-0809-0a0b0c0d0e0f" in [p["project_id"] for p in response.json()] -async def test_delete_project(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: + async def test_delete_project(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: - with asyncio_patch("gns3server.compute.project.Project.delete", return_value=True) as mock: - response = await compute_client.delete(app.url_path_for("compute:delete_compute_project", project_id=compute_project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert mock.called + with asyncio_patch("gns3server.compute.project.Project.delete", return_value=True) as mock: + response = await compute_client.delete(app.url_path_for("compute:delete_compute_project", project_id=compute_project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called -async def test_update_project(app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None: + async def test_update_project(self, app: FastAPI, compute_client: AsyncClient, base_params: dict) -> None: - response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params) - assert response.status_code == status.HTTP_201_CREATED + response = await compute_client.post(app.url_path_for("compute:create_compute_project"), json=base_params) + assert response.status_code == status.HTTP_201_CREATED - params = {"variables": [{"name": "TEST1", "value": "VAL1"}]} - response = await compute_client.put(app.url_path_for("compute:update_compute_project", project_id=base_params["project_id"]), - json=params) - assert response.status_code == status.HTTP_200_OK - assert response.json()["variables"] == [{"name": "TEST1", "value": "VAL1"}] + params = {"variables": [{"name": "TEST1", "value": "VAL1"}]} + response = await compute_client.put(app.url_path_for("compute:update_compute_project", project_id=base_params["project_id"]), + json=params) + assert response.status_code == status.HTTP_200_OK + assert response.json()["variables"] == [{"name": "TEST1", "value": "VAL1"}] -async def test_delete_project_invalid_uuid(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_delete_project_invalid_uuid(self, app: FastAPI, compute_client: AsyncClient) -> None: - response = await compute_client.delete(app.url_path_for("compute:delete_compute_project", project_id=str(uuid.uuid4()))) - assert response.status_code == status.HTTP_404_NOT_FOUND + response = await compute_client.delete(app.url_path_for("compute:delete_compute_project", project_id=str(uuid.uuid4()))) + assert response.status_code == status.HTTP_404_NOT_FOUND -async def test_close_project(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: + async def test_close_project(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: - with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:close_compute_project", project_id=compute_project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert mock.called + with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:close_compute_project", project_id=compute_project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called -# @pytest.mark.asyncio -# async def test_close_project_two_client_connected(compute_api, compute_project): -# -# ProjectHandler._notifications_listening = {compute_project.id: 2} -# with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock: -# response = await compute_client.post("/projects/{project_id}/close".format(project_id=compute_project.id)) -# assert response.status_code == status.HTTP_204_NO_CONTENT -# assert not mock.called + # @pytest.mark.asyncio + # async def test_close_project_two_client_connected(compute_api, compute_project): + # + # ProjectHandler._notifications_listening = {compute_project.id: 2} + # with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock: + # response = await compute_client.post("/projects/{project_id}/close".format(project_id=compute_project.id)) + # assert response.status_code == status.HTTP_204_NO_CONTENT + # assert not mock.called -async def test_close_project_invalid_uuid(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_close_project_invalid_uuid(self, app: FastAPI, compute_client: AsyncClient) -> None: - response = await compute_client.post(app.url_path_for("compute:close_compute_project", project_id=str(uuid.uuid4()))) - assert response.status_code == status.HTTP_404_NOT_FOUND + response = await compute_client.post(app.url_path_for("compute:close_compute_project", project_id=str(uuid.uuid4()))) + assert response.status_code == status.HTTP_404_NOT_FOUND -async def test_get_file(app: FastAPI, compute_client: AsyncClient) -> None: + async def test_get_file(self, app: FastAPI, compute_client: AsyncClient) -> None: - project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") + project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") - with open(os.path.join(project.path, "hello"), "w+") as f: - f.write("world") + with open(os.path.join(project.path, "hello"), "w+") as f: + f.write("world") - response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", project_id=project.id, file_path="hello")) - assert response.status_code == status.HTTP_200_OK - assert response.content == b"world" + response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", project_id=project.id, file_path="hello")) + assert response.status_code == status.HTTP_200_OK + assert response.content == b"world" - response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", project_id=project.id, file_path="false")) - assert response.status_code == status.HTTP_404_NOT_FOUND + response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", project_id=project.id, file_path="false")) + assert response.status_code == status.HTTP_404_NOT_FOUND - response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", - project_id=project.id, - file_path="../hello")) - assert response.status_code == status.HTTP_404_NOT_FOUND + response = await compute_client.get(app.url_path_for("compute:get_compute_project_file", + project_id=project.id, + file_path="../hello")) + assert response.status_code == status.HTTP_404_NOT_FOUND -async def test_get_file_forbidden_location(app: FastAPI, compute_client: AsyncClient, config, tmpdir) -> None: + async def test_get_file_forbidden_location( + self, + app: FastAPI, + compute_client: AsyncClient, + config, + tmpdir + ) -> None: - config.settings.Server.projects_path = str(tmpdir) - project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") - file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" - response = await compute_client.get( - app.url_path_for( - "compute:get_compute_project_file", - project_id=project.id, - file_path=file_path + config.settings.Server.projects_path = str(tmpdir) + project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") + file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" + response = await compute_client.get( + app.url_path_for( + "compute:get_compute_project_file", + project_id=project.id, + file_path=file_path + ) ) - ) - assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.status_code == status.HTTP_403_FORBIDDEN -async def test_write_file(app: FastAPI, compute_client: AsyncClient, config, tmpdir) -> None: + async def test_write_file(self, app: FastAPI, compute_client: AsyncClient, config, tmpdir) -> None: - config.settings.Server.projects_path = str(tmpdir) - project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") + config.settings.Server.projects_path = str(tmpdir) + project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") - response = await compute_client.post(app.url_path_for("compute:write_compute_project_file", - project_id=project.id, - file_path="hello"), content=b"world") - assert response.status_code == status.HTTP_204_NO_CONTENT + response = await compute_client.post(app.url_path_for("compute:write_compute_project_file", + project_id=project.id, + file_path="hello"), content=b"world") + assert response.status_code == status.HTTP_204_NO_CONTENT - with open(os.path.join(project.path, "hello")) as f: - assert f.read() == "world" + with open(os.path.join(project.path, "hello")) as f: + assert f.read() == "world" - response = await compute_client.post(app.url_path_for("compute:write_compute_project_file", - project_id=project.id, - file_path="../hello")) - assert response.status_code == status.HTTP_404_NOT_FOUND + response = await compute_client.post(app.url_path_for("compute:write_compute_project_file", + project_id=project.id, + file_path="../hello")) + assert response.status_code == status.HTTP_404_NOT_FOUND -async def test_write_file_forbidden_location(app: FastAPI, compute_client: AsyncClient, config, tmpdir) -> None: + async def test_write_file_forbidden_location( + self, + app: FastAPI, + compute_client: AsyncClient, + config, + tmpdir + ) -> None: - config.settings.Server.projects_path = str(tmpdir) - project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") + config.settings.Server.projects_path = str(tmpdir) + project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") - file_path = "%2e%2e/hello" - response = await compute_client.post(app.url_path_for("compute:write_compute_project_file", - project_id=project.id, - file_path=file_path), content=b"world") - assert response.status_code == status.HTTP_403_FORBIDDEN + file_path = "%2e%2e/hello" + response = await compute_client.post(app.url_path_for("compute:write_compute_project_file", + project_id=project.id, + file_path=file_path), content=b"world") + assert response.status_code == status.HTTP_403_FORBIDDEN diff --git a/tests/api/routes/compute/test_qemu_nodes.py b/tests/api/routes/compute/test_qemu_nodes.py index 61144fe2..464b5ddf 100644 --- a/tests/api/routes/compute/test_qemu_nodes.py +++ b/tests/api/routes/compute/test_qemu_nodes.py @@ -31,338 +31,327 @@ from gns3server.compute.project import Project pytestmark = pytest.mark.asyncio -@pytest.fixture -def fake_qemu_bin(monkeypatch, tmpdir) -> str: +class TestQemuNodesRoutes: - monkeypatch.setenv("PATH", str(tmpdir)) - bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64") - with open(bin_path, "w+") as f: - f.write("1") - os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) - return bin_path - - -@pytest.fixture -def fake_qemu_vm(images_dir) -> str: - - img_dir = os.path.join(images_dir, "QEMU") - bin_path = os.path.join(img_dir, "linux载.img") - with open(bin_path, "w+") as f: - f.write("1234567") - os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) - return bin_path - - -@pytest.fixture -def fake_qemu_img_binary(tmpdir): - - bin_path = str(tmpdir / "qemu-img") - with open(bin_path, "w+") as f: - f.write("1") - os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) - return bin_path - - -@pytest.fixture -def base_params(tmpdir, fake_qemu_bin) -> dict: - """Return standard parameters""" - - return {"name": "QEMU-TEST-1", "qemu_path": fake_qemu_bin} - - -@pytest_asyncio.fixture -async def qemu_vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, base_params: dict) -> None: - - response = await compute_client.post( - app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), - json=base_params - ) - assert response.status_code == status.HTTP_201_CREATED - return response.json() - - -async def test_qemu_create(app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - base_params: dict, - fake_qemu_bin: str) -> None: - - response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=base_params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "QEMU-TEST-1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["qemu_path"] == fake_qemu_bin - assert response.json()["platform"] == "x86_64" - - -async def test_qemu_create_platform(app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - base_params: dict, - fake_qemu_bin: str): - - base_params["qemu_path"] = None - base_params["platform"] = "x86_64" - response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=base_params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "QEMU-TEST-1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["qemu_path"] == fake_qemu_bin - assert response.json()["platform"] == "x86_64" - - -@pytest.mark.asyncio -async def test_qemu_create_with_params(app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - base_params: dict, - fake_qemu_vm: str): - - params = base_params - params["ram"] = 1024 - params["hda_disk_image"] = "linux载.img" - response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "QEMU-TEST-1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["ram"] == 1024 - assert response.json()["hda_disk_image"] == "linux载.img" - assert response.json()["hda_disk_image_md5sum"] == "fcea920f7412b5da7be0cf42b8c93759" - - -@pytest.mark.parametrize( - "name, status_code", - ( - ("valid-name.com", status.HTTP_201_CREATED), - ("42name", status.HTTP_201_CREATED), - ("424242", status.HTTP_409_CONFLICT), - ("name42", status.HTTP_201_CREATED), - ("name.424242", status.HTTP_409_CONFLICT), - ("-name", status.HTTP_409_CONFLICT), - ("name%-test", status.HTTP_409_CONFLICT), - ("x" * 63, status.HTTP_201_CREATED), - ("x" * 64, status.HTTP_409_CONFLICT), - (("x" * 62 + ".") * 4, status.HTTP_201_CREATED), - ("xx" + ("x" * 62 + ".") * 4, status.HTTP_409_CONFLICT), - ), -) -async def test_qemu_create_with_invalid_name( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - base_params: dict, - name: str, - status_code: int -) -> None: - - base_params["name"] = name - response = await compute_client.post( - app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=base_params - ) - assert response.status_code == status_code - -# async def test_qemu_create_with_project_file(app: FastAPI, -# compute_client: AsyncClient, -# compute_project: Project, -# base_params: dict, -# fake_qemu_vm: str) -> None: -# -# response = await compute_client.post( -# app.url_path_for("compute:write_compute_project_file", project_id=compute_project.id, file_path="hello.img"), -# content=b"world" -# ) -# assert response.status_code == status.HTTP_204_NO_CONTENT -# params = base_params -# params["hda_disk_image"] = "hello.img" -# response = await compute_client.post( -# app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), -# json=params -# ) -# assert response.status_code == status.HTTP_201_CREATED -# assert response.json()["hda_disk_image"] == "hello.img" -# assert response.json()["hda_disk_image_md5sum"] == "7d793037a0760186574b0282f2f435e7" - - -async def test_qemu_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, qemu_vm: dict): - - response = await compute_client.get( - app.url_path_for("compute:get_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) - ) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "QEMU-TEST-1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["node_directory"] == os.path.join( - compute_project.path, - "project-files", - "qemu", - qemu_vm["node_id"] - ) - - -async def test_qemu_start(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: - - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start", return_value=True) as mock: + @pytest.fixture + def fake_qemu_bin(self, monkeypatch, tmpdir) -> str: + + monkeypatch.setenv("PATH", str(tmpdir)) + bin_path = os.path.join(os.environ["PATH"], "qemu-system-x86_64") + with open(bin_path, "w+") as f: + f.write("1") + os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + return bin_path + + + @pytest.fixture + def fake_qemu_vm(self, images_dir) -> str: + + img_dir = os.path.join(images_dir, "QEMU") + bin_path = os.path.join(img_dir, "linux载.img") + with open(bin_path, "w+") as f: + f.write("1234567") + os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + return bin_path + + + @pytest.fixture + def fake_qemu_img_binary(self, tmpdir): + + bin_path = str(tmpdir / "qemu-img") + with open(bin_path, "w+") as f: + f.write("1") + os.chmod(bin_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + return bin_path + + + @pytest.fixture + def base_params(self, tmpdir, fake_qemu_bin) -> dict: + """Return standard parameters""" + + return {"name": "QEMU-TEST-1", "qemu_path": fake_qemu_bin} + + + @pytest_asyncio.fixture + async def qemu_vm( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict + ) -> None: + response = await compute_client.post( - app.url_path_for("compute:start_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) + app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), + json=base_params ) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_stop(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: - - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop", return_value=True) as mock: - response = await compute_client.post( - app.url_path_for("compute:stop_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) - ) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_reload(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: - - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.reload", return_value=True) as mock: - response = await compute_client.post( - app.url_path_for("compute:reload_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) - ) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_suspend(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: - - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.suspend", return_value=True) as mock: - response = await compute_client.post( - app.url_path_for("compute:suspend_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) - ) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_resume(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: - - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.resume", return_value=True) as mock: - response = await compute_client.post( - app.url_path_for("compute:resume_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) - ) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_delete(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: - - with asyncio_patch("gns3server.compute.qemu.Qemu.delete_node", return_value=True) as mock: - response = await compute_client.delete( - app.url_path_for("compute:delete_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) - ) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_update(app: FastAPI, - compute_client: AsyncClient, - qemu_vm: dict, - free_console_port: int, - fake_qemu_vm: str) -> None: - - params = { - "name": "test", - "console": free_console_port, - "ram": 1024, - "hdb_disk_image": "linux载.img" - } - - response = await compute_client.put( - app.url_path_for("compute:update_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]), - json=params + assert response.status_code == status.HTTP_201_CREATED + return response.json() + + + async def test_qemu_create( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict, + fake_qemu_bin: str + ) -> None: + + response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=base_params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "QEMU-TEST-1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["qemu_path"] == fake_qemu_bin + assert response.json()["platform"] == "x86_64" + + + async def test_qemu_create_platform( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict, + fake_qemu_bin: str + ): + + base_params["qemu_path"] = None + base_params["platform"] = "x86_64" + response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=base_params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "QEMU-TEST-1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["qemu_path"] == fake_qemu_bin + assert response.json()["platform"] == "x86_64" + + + @pytest.mark.asyncio + async def test_qemu_create_with_params( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict, + fake_qemu_vm: str + ): + + params = base_params + params["ram"] = 1024 + params["hda_disk_image"] = "linux载.img" + response = await compute_client.post(app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "QEMU-TEST-1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["ram"] == 1024 + assert response.json()["hda_disk_image"] == "linux载.img" + assert response.json()["hda_disk_image_md5sum"] == "fcea920f7412b5da7be0cf42b8c93759" + + + @pytest.mark.parametrize( + "name, status_code", + ( + ("valid-name.com", status.HTTP_201_CREATED), + ("42name", status.HTTP_201_CREATED), + ("424242", status.HTTP_409_CONFLICT), + ("name42", status.HTTP_201_CREATED), + ("name.424242", status.HTTP_409_CONFLICT), + ("-name", status.HTTP_409_CONFLICT), + ("name%-test", status.HTTP_409_CONFLICT), + ("x" * 63, status.HTTP_201_CREATED), + ("x" * 64, status.HTTP_409_CONFLICT), + (("x" * 62 + ".") * 4, status.HTTP_201_CREATED), + ("xx" + ("x" * 62 + ".") * 4, status.HTTP_409_CONFLICT), + ), ) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - assert response.json()["console"] == free_console_port - assert response.json()["hdb_disk_image"] == "linux载.img" - assert response.json()["ram"] == 1024 - - -async def test_qemu_nio_create_udp(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.add_ubridge_udp_connection"): + async def test_qemu_create_with_invalid_name( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + base_params: dict, + name: str, + status_code: int + ) -> None: + + base_params["name"] = name + response = await compute_client.post( + app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), json=base_params + ) + assert response.status_code == status_code + + # async def test_qemu_create_with_project_file(self, app: FastAPI, + # compute_client: AsyncClient, + # compute_project: Project, + # base_params: dict, + # fake_qemu_vm: str) -> None: + # + # response = await compute_client.post( + # app.url_path_for("compute:write_compute_project_file", project_id=compute_project.id, file_path="hello.img"), + # content=b"world" + # ) + # assert response.status_code == status.HTTP_204_NO_CONTENT + # params = base_params + # params["hda_disk_image"] = "hello.img" + # response = await compute_client.post( + # app.url_path_for("compute:create_qemu_node", project_id=compute_project.id), + # json=params + # ) + # assert response.status_code == status.HTTP_201_CREATED + # assert response.json()["hda_disk_image"] == "hello.img" + # assert response.json()["hda_disk_image_md5sum"] == "7d793037a0760186574b0282f2f435e7" + + + async def test_qemu_get( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + qemu_vm: dict + ): + + response = await compute_client.get( + app.url_path_for("compute:get_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) + ) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "QEMU-TEST-1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["node_directory"] == os.path.join( + compute_project.path, + "project-files", + "qemu", + qemu_vm["node_id"] + ) + + + async def test_qemu_start(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: + + with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start", return_value=True) as mock: + response = await compute_client.post( + app.url_path_for("compute:start_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) + ) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_qemu_stop(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: + + with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop", return_value=True) as mock: + response = await compute_client.post( + app.url_path_for("compute:stop_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) + ) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_qemu_reload(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: + + with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.reload", return_value=True) as mock: + response = await compute_client.post( + app.url_path_for("compute:reload_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) + ) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_qemu_suspend(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: + + with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.suspend", return_value=True) as mock: + response = await compute_client.post( + app.url_path_for("compute:suspend_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) + ) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_qemu_resume(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: + + with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.resume", return_value=True) as mock: + response = await compute_client.post( + app.url_path_for("compute:resume_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) + ) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_qemu_delete(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: + + with asyncio_patch("gns3server.compute.qemu.Qemu.delete_node", return_value=True) as mock: + response = await compute_client.delete( + app.url_path_for("compute:delete_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]) + ) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_qemu_update( + self, + app: FastAPI, + compute_client: AsyncClient, + qemu_vm: dict, + free_console_port: int, + fake_qemu_vm: str + ) -> None: + + params = { + "name": "test", + "console": free_console_port, + "ram": 1024, + "hdb_disk_image": "linux载.img" + } + + response = await compute_client.put( + app.url_path_for("compute:update_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]), + json=params + ) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + assert response.json()["console"] == free_console_port + assert response.json()["hdb_disk_image"] == "linux载.img" + assert response.json()["ram"] == 1024 + + + async def test_qemu_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.add_ubridge_udp_connection"): + await compute_client.put( + app.url_path_for("compute:update_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]), + json={"adapters": 2} + ) + + url = app.url_path_for( + "compute:create_qemu_node_nio", + project_id=qemu_vm["project_id"], + node_id=qemu_vm["node_id"], + adapter_number="1", + port_number="0" + ) + response = await compute_client.post(url, json=params) + + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + async def test_qemu_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + await compute_client.put( app.url_path_for("compute:update_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]), json={"adapters": 2} ) - - url = app.url_path_for( - "compute:create_qemu_node_nio", - project_id=qemu_vm["project_id"], - node_id=qemu_vm["node_id"], - adapter_number="1", - port_number="0" - ) - response = await compute_client.post(url, json=params) - - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_qemu_nio_update_udp(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - await compute_client.put( - app.url_path_for("compute:update_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]), - json={"adapters": 2} - ) - - url = app.url_path_for( - "compute:create_qemu_node_nio", - project_id=qemu_vm["project_id"], - node_id=qemu_vm["node_id"], - adapter_number="1", - port_number="0" - ) - - await compute_client.post(url, json=params) - params["filters"] = {} - - url = app.url_path_for( - "compute:update_qemu_node_nio", - project_id=qemu_vm["project_id"], - node_id=qemu_vm["node_id"], - adapter_number="1", - port_number="0" - ) - response = await compute_client.put(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_qemu_delete_nio(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._ubridge_send"): - await compute_client.put( - app.url_path_for("compute:update_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]), - json={"adapters": 2} - ) - + url = app.url_path_for( "compute:create_qemu_node_nio", project_id=qemu_vm["project_id"], @@ -370,192 +359,285 @@ async def test_qemu_delete_nio(app: FastAPI, compute_client: AsyncClient, qemu_v adapter_number="1", port_number="0" ) + await compute_client.post(url, json=params) - + params["filters"] = {} + url = app.url_path_for( - "compute:delete_qemu_node_nio", + "compute:update_qemu_node_nio", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"], adapter_number="1", port_number="0" ) - response = await compute_client.delete(url) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_images(app: FastAPI, compute_client: AsyncClient, fake_qemu_vm) -> None: - - response = await compute_client.get(app.url_path_for("compute:get_qemu_images")) - assert response.status_code == status.HTTP_200_OK - print(response.json()) - assert {"filename": "linux载.img", "path": "linux载.img", "md5sum": "fcea920f7412b5da7be0cf42b8c93759", "filesize": 7} in response.json() - - -async def test_upload_image(app: FastAPI, compute_client: AsyncClient, tmpdir: str) -> None: - - with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)): - - response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", - filename="test2使"), content=b"TEST") + response = await compute_client.put(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + async def test_qemu_delete_nio(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._ubridge_send"): + await compute_client.put( + app.url_path_for("compute:update_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]), + json={"adapters": 2} + ) + + url = app.url_path_for( + "compute:create_qemu_node_nio", + project_id=qemu_vm["project_id"], + node_id=qemu_vm["node_id"], + adapter_number="1", + port_number="0" + ) + await compute_client.post(url, json=params) + + url = app.url_path_for( + "compute:delete_qemu_node_nio", + project_id=qemu_vm["project_id"], + node_id=qemu_vm["node_id"], + adapter_number="1", + port_number="0" + ) + response = await compute_client.delete(url) assert response.status_code == status.HTTP_204_NO_CONTENT - - with open(str(tmpdir / "test2使")) as f: - assert f.read() == "TEST" - - with open(str(tmpdir / "test2使.md5sum")) as f: - checksum = f.read() - assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" - - -async def test_upload_image_ova(app: FastAPI, compute_client: AsyncClient, tmpdir:str) -> None: - - with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)): - + + + async def test_images(self, app: FastAPI, compute_client: AsyncClient, fake_qemu_vm) -> None: + + response = await compute_client.get(app.url_path_for("compute:get_qemu_images")) + assert response.status_code == status.HTTP_200_OK + print(response.json()) + assert {"filename": "linux载.img", "path": "linux载.img", "md5sum": "fcea920f7412b5da7be0cf42b8c93759", "filesize": 7} in response.json() + + + async def test_upload_image(self, app: FastAPI, compute_client: AsyncClient, tmpdir: str) -> None: + + with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)): + + response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", + filename="test2使"), content=b"TEST") + assert response.status_code == status.HTTP_204_NO_CONTENT + + with open(str(tmpdir / "test2使")) as f: + assert f.read() == "TEST" + + with open(str(tmpdir / "test2使.md5sum")) as f: + checksum = f.read() + assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" + + + async def test_upload_image_ova(self, app: FastAPI, compute_client: AsyncClient, tmpdir:str) -> None: + + with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)): + + response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", + filename="test2.ova/test2.vmdk"), content=b"TEST") + assert response.status_code == status.HTTP_204_NO_CONTENT + + with open(str(tmpdir / "test2.ova" / "test2.vmdk")) as f: + assert f.read() == "TEST" + + with open(str(tmpdir / "test2.ova" / "test2.vmdk.md5sum")) as f: + checksum = f.read() + assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" + + + async def test_upload_image_forbidden_location( + self, app: FastAPI, + compute_client: AsyncClient, + tmpdir: str + ) -> None: + response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", - filename="test2.ova/test2.vmdk"), content=b"TEST") + filename="/qemu/images/../../test2"), content=b"TEST") + assert response.status_code == status.HTTP_403_FORBIDDEN + + + async def test_download_image(self, app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: + + response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", filename="test3"), content=b"TEST") assert response.status_code == status.HTTP_204_NO_CONTENT - - with open(str(tmpdir / "test2.ova" / "test2.vmdk")) as f: - assert f.read() == "TEST" - - with open(str(tmpdir / "test2.ova" / "test2.vmdk.md5sum")) as f: - checksum = f.read() - assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" - - -async def test_upload_image_forbidden_location(app: FastAPI, compute_client: AsyncClient, tmpdir: str) -> None: - - response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", - filename="/qemu/images/../../test2"), content=b"TEST") - assert response.status_code == status.HTTP_403_FORBIDDEN - - -async def test_download_image(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: - - response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", filename="test3"), content=b"TEST") - assert response.status_code == status.HTTP_204_NO_CONTENT - - response = await compute_client.get(app.url_path_for("compute:download_qemu_image", filename="test3")) - assert response.status_code == status.HTTP_200_OK - - -async def test_download_image_forbidden_location(app: FastAPI, compute_client: AsyncClient, tmpdir) -> None: - - file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" - response = await compute_client.get(app.url_path_for("compute:download_qemu_image", filename=file_path)) - assert response.status_code == status.HTTP_403_FORBIDDEN - - -@pytest.mark.skipif(os.getuid() == 0, reason="Root can delete any image") -async def test_upload_image_permission_denied(app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: - - with open(os.path.join(images_dir, "QEMU", "test2.tmp"), "w+") as f: - f.write("") - os.chmod(os.path.join(images_dir, "QEMU", "test2.tmp"), 0) - - response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", filename="test2"), content=b"TEST") - assert response.status_code == status.HTTP_409_CONFLICT - - -# @pytest.mark.asyncio -# async def test_create_img_relative(app: FastAPI, compute_client: AsyncClient): -# -# params = { -# "qemu_img": "/tmp/qemu-img", -# "path": "hda.qcow2", -# "format": "qcow2", -# "preallocation": "metadata", -# "cluster_size": 64, -# "refcount_bits": 12, -# "lazy_refcounts": "off", -# "size": 100 -# } -# with asyncio_patch("gns3server.compute.Qemu.create_disk"): -# response = await compute_client.post(app.url_path_for("compute:create_qemu_image"), json=params) -# assert response.status_code == status.HTTP_204_NO_CONTENT -# -# -# async def test_create_img_absolute_non_local(app: FastAPI, compute_client: AsyncClient, config) -> None: -# -# config.settings.Server.local = False -# params = { -# "qemu_img": "/tmp/qemu-img", -# "path": "/tmp/hda.qcow2", -# "format": "qcow2", -# "preallocation": "metadata", -# "cluster_size": 64, -# "refcount_bits": 12, -# "lazy_refcounts": "off", -# "size": 100 -# } -# with asyncio_patch("gns3server.compute.Qemu.create_disk"): -# response = await compute_client.post(app.url_path_for("compute:create_qemu_image"), json=params) -# assert response.status_code == 403 -# -# -# async def test_create_img_absolute_local(app: FastAPI, compute_client: AsyncClient, config) -> None: -# -# params = { -# "qemu_img": "/tmp/qemu-img", -# "path": "/tmp/hda.qcow2", -# "format": "qcow2", -# "preallocation": "metadata", -# "cluster_size": 64, -# "refcount_bits": 12, -# "lazy_refcounts": "off", -# "size": 100 -# } -# with asyncio_patch("gns3server.compute.Qemu.create_disk"): -# response = await compute_client.post(app.url_path_for("compute:create_qemu_image"), json=params) -# assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_capabilities(app: FastAPI, compute_client: AsyncClient) -> None: - - with asyncio_patch("gns3server.compute.Qemu.get_kvm_archs", return_value=["x86_64"]): - response = await compute_client.get(app.url_path_for("compute:get_qemu_capabilities")) - assert response.json()["kvm"] == ["x86_64"] - - -async def test_qemu_duplicate(app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - qemu_vm: dict, - base_params: dict) -> None: - - # create destination node first - response = await compute_client.post( - app.url_path_for("compute:create_qemu_node", project_id=qemu_vm["project_id"]), - json=base_params - ) - - assert response.status_code == status.HTTP_201_CREATED - params = {"destination_node_id": response.json()["node_id"]} - response = await compute_client.post( - app.url_path_for("compute:duplicate_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]), - json=params - ) - assert response.status_code == status.HTTP_201_CREATED - - -async def test_qemu_create_disk_image( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - fake_qemu_img_binary: str, - qemu_vm: dict, -): - - options = { - "format": "qcow2", - "preallocation": "metadata", - "cluster_size": 64, - "refcount_bits": 12, - "lazy_refcounts": "off", - "size": 30 - } - - with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as qemu_img: + + response = await compute_client.get(app.url_path_for("compute:download_qemu_image", filename="test3")) + assert response.status_code == status.HTTP_200_OK + + + async def test_download_image_forbidden_location(self, app: FastAPI, compute_client: AsyncClient, tmpdir) -> None: + + file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" + response = await compute_client.get(app.url_path_for("compute:download_qemu_image", filename=file_path)) + assert response.status_code == status.HTTP_403_FORBIDDEN + + + @pytest.mark.skipif(os.getuid() == 0, reason="Root can delete any image") + async def test_upload_image_permission_denied(self, app: FastAPI, compute_client: AsyncClient, images_dir: str) -> None: + + with open(os.path.join(images_dir, "QEMU", "test2.tmp"), "w+") as f: + f.write("") + os.chmod(os.path.join(images_dir, "QEMU", "test2.tmp"), 0) + + response = await compute_client.post(app.url_path_for("compute:upload_qemu_image", filename="test2"), content=b"TEST") + assert response.status_code == status.HTTP_409_CONFLICT + + + # @pytest.mark.asyncio + # async def test_create_img_relative(self, app: FastAPI, compute_client: AsyncClient): + # + # params = { + # "qemu_img": "/tmp/qemu-img", + # "path": "hda.qcow2", + # "format": "qcow2", + # "preallocation": "metadata", + # "cluster_size": 64, + # "refcount_bits": 12, + # "lazy_refcounts": "off", + # "size": 100 + # } + # with asyncio_patch("gns3server.compute.Qemu.create_disk"): + # response = await compute_client.post(app.url_path_for("compute:create_qemu_image"), json=params) + # assert response.status_code == status.HTTP_204_NO_CONTENT + # + # + # async def test_create_img_absolute_non_local(self, app: FastAPI, compute_client: AsyncClient, config) -> None: + # + # config.settings.Server.local = False + # params = { + # "qemu_img": "/tmp/qemu-img", + # "path": "/tmp/hda.qcow2", + # "format": "qcow2", + # "preallocation": "metadata", + # "cluster_size": 64, + # "refcount_bits": 12, + # "lazy_refcounts": "off", + # "size": 100 + # } + # with asyncio_patch("gns3server.compute.Qemu.create_disk"): + # response = await compute_client.post(app.url_path_for("compute:create_qemu_image"), json=params) + # assert response.status_code == 403 + # + # + # async def test_create_img_absolute_local(self, app: FastAPI, compute_client: AsyncClient, config) -> None: + # + # params = { + # "qemu_img": "/tmp/qemu-img", + # "path": "/tmp/hda.qcow2", + # "format": "qcow2", + # "preallocation": "metadata", + # "cluster_size": 64, + # "refcount_bits": 12, + # "lazy_refcounts": "off", + # "size": 100 + # } + # with asyncio_patch("gns3server.compute.Qemu.create_disk"): + # response = await compute_client.post(app.url_path_for("compute:create_qemu_image"), json=params) + # assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_capabilities(self, app: FastAPI, compute_client: AsyncClient) -> None: + + with asyncio_patch("gns3server.compute.Qemu.get_kvm_archs", return_value=["x86_64"]): + response = await compute_client.get(app.url_path_for("compute:get_qemu_capabilities")) + assert response.json()["kvm"] == ["x86_64"] + + + async def test_qemu_duplicate( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + qemu_vm: dict, + base_params: dict + ) -> None: + + # create destination node first + response = await compute_client.post( + app.url_path_for("compute:create_qemu_node", project_id=qemu_vm["project_id"]), + json=base_params + ) + + assert response.status_code == status.HTTP_201_CREATED + params = {"destination_node_id": response.json()["node_id"]} + response = await compute_client.post( + app.url_path_for("compute:duplicate_qemu_node", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"]), + json=params + ) + assert response.status_code == status.HTTP_201_CREATED + + + async def test_qemu_create_disk_image( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + fake_qemu_img_binary: str, + qemu_vm: dict, + ): + + options = { + "format": "qcow2", + "preallocation": "metadata", + "cluster_size": 64, + "refcount_bits": 12, + "lazy_refcounts": "off", + "size": 30 + } + + with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as qemu_img: + response = await compute_client.post( + app.url_path_for( + "compute:create_qemu_disk_image", + project_id=qemu_vm["project_id"], + node_id=qemu_vm["node_id"], + disk_name="disk.qcow2" + ), + json=options + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + args, kwargs = qemu_img.call_args + assert args == ( + fake_qemu_img_binary, + "create", + "-f", + "qcow2", + "-o", + "cluster_size=64", + "-o", + "lazy_refcounts=off", + "-o", + "preallocation=metadata", + "-o", + "refcount_bits=12", + os.path.join(qemu_vm["node_directory"], "disk.qcow2"), + "30M" + ) + + + async def test_qemu_create_disk_image_already_exists( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + fake_qemu_img_binary: str, + qemu_vm: dict, + ): + + node = compute_project.get_node(qemu_vm["node_id"]) + shutil.copy("tests/resources/empty8G.qcow2", os.path.join(node.working_dir, "disk.qcow2")) + + options = { + "format": "qcow2", + "size": 100 + } + response = await compute_client.post( app.url_path_for( "compute:create_qemu_disk_image", @@ -565,202 +647,159 @@ async def test_qemu_create_disk_image( ), json=options ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - args, kwargs = qemu_img.call_args - assert args == ( - fake_qemu_img_binary, - "create", - "-f", - "qcow2", - "-o", - "cluster_size=64", - "-o", - "lazy_refcounts=off", - "-o", - "preallocation=metadata", - "-o", - "refcount_bits=12", - os.path.join(qemu_vm["node_directory"], "disk.qcow2"), - "30M" - ) - - -async def test_qemu_create_disk_image_already_exists( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - fake_qemu_img_binary: str, - qemu_vm: dict, -): - - node = compute_project.get_node(qemu_vm["node_id"]) - shutil.copy("tests/resources/empty8G.qcow2", os.path.join(node.working_dir, "disk.qcow2")) - - options = { - "format": "qcow2", - "size": 100 - } - - response = await compute_client.post( - app.url_path_for( - "compute:create_qemu_disk_image", - project_id=qemu_vm["project_id"], - node_id=qemu_vm["node_id"], - disk_name="disk.qcow2" - ), - json=options - ) - assert response.status_code == status.HTTP_409_CONFLICT - - -# async def test_qemu_create_disk_image_with_not_supported_characters_by_filesystem( -# app: FastAPI, -# compute_client: AsyncClient, -# compute_project: Project, -# fake_qemu_img_binary: str, -# qemu_vm: dict, -# ): -# -# node = compute_project.get_node(qemu_vm["node_id"]) -# shutil.copy("tests/resources/empty8G.qcow2", os.path.join(node.working_dir, "disk.qcow2")) -# -# options = { -# "format": "qcow2", -# "size": 100 -# } -# -# with patch("os.path.exists", side_effect=UnicodeEncodeError('error', u"", 1, 2, 'Emulated Unicode Err')): -# response = await compute_client.post( -# app.url_path_for( -# "compute:create_qemu_disk_image", -# project_id=qemu_vm["project_id"], -# node_id=qemu_vm["node_id"], -# disk_name=u"\u2019" -# ), -# json=options -# ) -# assert response.status_code == status.HTTP_409_CONFLICT - - -async def test_qemu_update_disk_image( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - fake_qemu_img_binary: str, - qemu_vm: dict, -) -> None: - - node = compute_project.get_node(qemu_vm["node_id"]) - shutil.copy("tests/resources/empty8G.qcow2", os.path.join(node.working_dir, "disk.qcow2")) - - with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as qemu_img: - response = await compute_client.put( + assert response.status_code == status.HTTP_409_CONFLICT + + + # async def test_qemu_create_disk_image_with_not_supported_characters_by_filesystem( + # app: FastAPI, + # compute_client: AsyncClient, + # compute_project: Project, + # fake_qemu_img_binary: str, + # qemu_vm: dict, + # ): + # + # node = compute_project.get_node(qemu_vm["node_id"]) + # shutil.copy("tests/resources/empty8G.qcow2", os.path.join(node.working_dir, "disk.qcow2")) + # + # options = { + # "format": "qcow2", + # "size": 100 + # } + # + # with patch("os.path.exists", side_effect=UnicodeEncodeError('error', u"", 1, 2, 'Emulated Unicode Err')): + # response = await compute_client.post( + # app.url_path_for( + # "compute:create_qemu_disk_image", + # project_id=qemu_vm["project_id"], + # node_id=qemu_vm["node_id"], + # disk_name=u"\u2019" + # ), + # json=options + # ) + # assert response.status_code == status.HTTP_409_CONFLICT + + + async def test_qemu_update_disk_image( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + fake_qemu_img_binary: str, + qemu_vm: dict, + ) -> None: + + node = compute_project.get_node(qemu_vm["node_id"]) + shutil.copy("tests/resources/empty8G.qcow2", os.path.join(node.working_dir, "disk.qcow2")) + + with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as qemu_img: + response = await compute_client.put( + app.url_path_for( + "compute:update_qemu_disk_image", + project_id=qemu_vm["project_id"], + node_id=qemu_vm["node_id"], + disk_name="disk.qcow2" + ), + json={"extend": 10} + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + assert qemu_img.called + args, kwargs = qemu_img.call_args + assert args == ( + fake_qemu_img_binary, + "resize", + os.path.join(qemu_vm["node_directory"], "disk.qcow2"), + "+10M" + ) + + + async def test_qemu_delete_disk_image( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + qemu_vm: dict, + ) -> None: + + node = compute_project.get_node(qemu_vm["node_id"]) + shutil.copy("tests/resources/empty8G.qcow2", os.path.join(node.working_dir, "disk.qcow2")) + + response = await compute_client.delete( app.url_path_for( - "compute:update_qemu_disk_image", + "compute:delete_qemu_disk_image", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"], disk_name="disk.qcow2" - ), - json={"extend": 10} + ) ) assert response.status_code == status.HTTP_204_NO_CONTENT - - assert qemu_img.called - args, kwargs = qemu_img.call_args - assert args == ( - fake_qemu_img_binary, - "resize", - os.path.join(qemu_vm["node_directory"], "disk.qcow2"), - "+10M" + + + async def test_qemu_delete_disk_image_missing_image( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + qemu_vm: dict, + ) -> None: + + response = await compute_client.delete( + app.url_path_for( + "compute:delete_qemu_disk_image", + project_id=qemu_vm["project_id"], + node_id=qemu_vm["node_id"], + disk_name="unknown_image.qcow2" + ) ) - - -async def test_qemu_delete_disk_image( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - qemu_vm: dict, -) -> None: - - node = compute_project.get_node(qemu_vm["node_id"]) - shutil.copy("tests/resources/empty8G.qcow2", os.path.join(node.working_dir, "disk.qcow2")) - - response = await compute_client.delete( - app.url_path_for( - "compute:delete_qemu_disk_image", + assert response.status_code == status.HTTP_409_CONFLICT + + + @pytest.mark.asyncio + async def test_qemu_start_capture(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict): + + params = { + "capture_file_name": "test.pcap", + "data_link_type": "DLT_EN10MB" + } + + url = app.url_path_for( + "compute:start_qemu_node_capture", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"], - disk_name="disk.qcow2" + adapter_number="0", + port_number="0" ) - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_delete_disk_image_missing_image( - app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - qemu_vm: dict, -) -> None: - - response = await compute_client.delete( - app.url_path_for( - "compute:delete_qemu_disk_image", + + with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start_capture") as mock: + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_200_OK + assert mock.called + assert "test.pcap" in response.json()["pcap_file_path"] + + + @pytest.mark.asyncio + async def test_qemu_stop_capture(self, app: FastAPI, compute_client: AsyncClient, qemu_vm: dict): + + url = app.url_path_for( + "compute:stop_qemu_node_capture", project_id=qemu_vm["project_id"], node_id=qemu_vm["node_id"], - disk_name="unknown_image.qcow2" + adapter_number="0", + port_number="0" ) - ) - assert response.status_code == status.HTTP_409_CONFLICT - - -@pytest.mark.asyncio -async def test_qemu_start_capture(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict): - - params = { - "capture_file_name": "test.pcap", - "data_link_type": "DLT_EN10MB" - } - - url = app.url_path_for( - "compute:start_qemu_node_capture", - project_id=qemu_vm["project_id"], - node_id=qemu_vm["node_id"], - adapter_number="0", - port_number="0" - ) - - with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start_capture") as mock: - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_200_OK - assert mock.called - assert "test.pcap" in response.json()["pcap_file_path"] - - -@pytest.mark.asyncio -async def test_qemu_stop_capture(app: FastAPI, compute_client: AsyncClient, qemu_vm: dict): - - url = app.url_path_for( - "compute:stop_qemu_node_capture", - project_id=qemu_vm["project_id"], - node_id=qemu_vm["node_id"], - adapter_number="0", - port_number="0" - ) - - with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop_capture") as mock: - response = await compute_client.post(url) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert mock.called - - -# @pytest.mark.asyncio -# async def test_qemu_pcap(app: FastAPI, compute_client: AsyncClient, vm, compute_project): -# -# with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.get_nio"): -# with asyncio_patch("gns3server.compute.qemu.Qemu.stream_pcap_file"): -# response = await compute_client.get("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) -# assert response.status_code == status.HTTP_200_OK + + with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop_capture") as mock: + response = await compute_client.post(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called + + + # @pytest.mark.asyncio + # async def test_qemu_pcap(self, app: FastAPI, compute_client: AsyncClient, vm, compute_project): + # + # with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.get_nio"): + # with asyncio_patch("gns3server.compute.qemu.Qemu.stream_pcap_file"): + # response = await compute_client.get("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) + # assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/compute/test_virtualbox_nodes.py b/tests/api/routes/compute/test_virtualbox_nodes.py index 5a88b6d7..e29f104d 100644 --- a/tests/api/routes/compute/test_virtualbox_nodes.py +++ b/tests/api/routes/compute/test_virtualbox_nodes.py @@ -28,221 +28,223 @@ from gns3server.compute.project import Project pytestmark = pytest.mark.asyncio -@pytest_asyncio.fixture(scope="function") -async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: - - vboxmanage_path = "/fake/VboxManage" - params = { - "name": "VMTEST", - "vmname": "VMTEST", - "linked_clone": False - } - - with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:create_virtualbox_node", project_id=compute_project.id), - json=params) - assert mock.called - assert response.status_code == status.HTTP_201_CREATED - - with patch("gns3server.compute.virtualbox.VirtualBox.find_vboxmanage", return_value=vboxmanage_path): - return response.json() - - -async def test_vbox_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: - - params = { - "name": "VM1", - "vmname": "VM1", - "linked_clone": False - } - - with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True): - response = await compute_client.post(app.url_path_for("compute:create_virtualbox_node", project_id=compute_project.id), - json=params) +class TestVirtualBoxNodesRoutes: + + @pytest_asyncio.fixture + async def vm(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: + + vboxmanage_path = "/fake/VboxManage" + params = { + "name": "VMTEST", + "vmname": "VMTEST", + "linked_clone": False + } + + with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:create_virtualbox_node", project_id=compute_project.id), + json=params) + assert mock.called assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "VM1" + + with patch("gns3server.compute.virtualbox.VirtualBox.find_vboxmanage", return_value=vboxmanage_path): + return response.json() + + + async def test_vbox_create(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: + + params = { + "name": "VM1", + "vmname": "VM1", + "linked_clone": False + } + + with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True): + response = await compute_client.post(app.url_path_for("compute:create_virtualbox_node", project_id=compute_project.id), + json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "VM1" + assert response.json()["project_id"] == compute_project.id + + + async def test_vbox_get(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: + + response = await compute_client.get(app.url_path_for("compute:get_virtualbox_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "VMTEST" assert response.json()["project_id"] == compute_project.id - - -async def test_vbox_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: - - response = await compute_client.get(app.url_path_for("compute:get_virtualbox_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "VMTEST" - assert response.json()["project_id"] == compute_project.id - - -async def test_vbox_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start", return_value=True) as mock: - - response = await compute_client.post(app.url_path_for("compute:start_virtualbox_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vbox_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:stop_virtualbox_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vbox_suspend(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.suspend", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:suspend_virtualbox_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vbox_resume(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.resume", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:resume_virtualbox_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vbox_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.reload", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:reload_virtualbox_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vbox_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for("compute:create_virtualbox_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock: - response = await compute_client.post(url, json=params) - assert mock.called - args, kwgars = mock.call_args - assert args[0] == 0 - - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -# @pytest.mark.asyncio -# async def test_vbox_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm): -# -# params = { -# "type": "nio_udp", -# "lport": 4242, -# "rport": 4343, -# "rhost": "127.0.0.1", -# "filters": {} -# } -# -# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.ethernet_adapters'): -# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding'): -# response = await compute_client.put("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) -# -# assert response.status_code == status.HTTP_201_CREATED -# assert response.json()["type"] == "nio_udp" - - -async def test_vbox_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - url = app.url_path_for("compute:delete_virtualbox_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: - response = await compute_client.delete(url) - assert mock.called - args, kwgars = mock.call_args - assert args[0] == 0 - assert response.status_code == status.HTTP_204_NO_CONTENT - - -@pytest.mark.asyncio -async def test_vbox_update(app: FastAPI, compute_client: AsyncClient, vm, free_console_port): - - params = { - "name": "test", - "console": free_console_port - } - - response = await compute_client.put(app.url_path_for("compute:update_virtualbox_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json=params) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - assert response.json()["console"] == free_console_port - - -@pytest.mark.asyncio -async def test_virtualbox_start_capture(app: FastAPI, compute_client: AsyncClient, vm): - - params = { - "capture_file_name": "test.pcap", - "data_link_type": "DLT_EN10MB" - } - - url = app.url_path_for("compute:start_virtualbox_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start_capture") as mock: - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_200_OK + + + async def test_vbox_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start", return_value=True) as mock: + + response = await compute_client.post(app.url_path_for("compute:start_virtualbox_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) assert mock.called - assert "test.pcap" in response.json()["pcap_file_path"] - - -@pytest.mark.asyncio -async def test_virtualbox_stop_capture(app: FastAPI, compute_client: AsyncClient, vm): - - url = app.url_path_for("compute:stop_virtualbox_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop_capture") as mock: - response = await compute_client.post(url) assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vbox_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:stop_virtualbox_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) assert mock.called - - -# @pytest.mark.asyncio -# async def test_virtualbox_pcap(app: FastAPI, compute_client: AsyncClient, vm, compute_project): -# -# with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.get_nio"): -# with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.stream_pcap_file"): -# response = await compute_client.get("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) -# assert response.status_code == status.HTTP_200_OK + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vbox_suspend(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.suspend", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:suspend_virtualbox_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vbox_resume(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.resume", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:resume_virtualbox_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vbox_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.reload", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:reload_virtualbox_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vbox_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + url = app.url_path_for("compute:create_virtualbox_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock: + response = await compute_client.post(url, json=params) + assert mock.called + args, kwgars = mock.call_args + assert args[0] == 0 + + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + # @pytest.mark.asyncio + # async def test_vbox_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm): + # + # params = { + # "type": "nio_udp", + # "lport": 4242, + # "rport": 4343, + # "rhost": "127.0.0.1", + # "filters": {} + # } + # + # with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.ethernet_adapters'): + # with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding'): + # response = await compute_client.put("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) + # + # assert response.status_code == status.HTTP_201_CREATED + # assert response.json()["type"] == "nio_udp" + + + async def test_vbox_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + url = app.url_path_for("compute:delete_virtualbox_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: + response = await compute_client.delete(url) + assert mock.called + args, kwgars = mock.call_args + assert args[0] == 0 + assert response.status_code == status.HTTP_204_NO_CONTENT + + + @pytest.mark.asyncio + async def test_vbox_update(self, app: FastAPI, compute_client: AsyncClient, vm, free_console_port): + + params = { + "name": "test", + "console": free_console_port + } + + response = await compute_client.put(app.url_path_for("compute:update_virtualbox_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json=params) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + assert response.json()["console"] == free_console_port + + + @pytest.mark.asyncio + async def test_virtualbox_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm): + + params = { + "capture_file_name": "test.pcap", + "data_link_type": "DLT_EN10MB" + } + + url = app.url_path_for("compute:start_virtualbox_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start_capture") as mock: + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_200_OK + assert mock.called + assert "test.pcap" in response.json()["pcap_file_path"] + + + @pytest.mark.asyncio + async def test_virtualbox_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm): + + url = app.url_path_for("compute:stop_virtualbox_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop_capture") as mock: + response = await compute_client.post(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called + + + # @pytest.mark.asyncio + # async def test_virtualbox_pcap(self, app: FastAPI, compute_client: AsyncClient, vm, compute_project): + # + # with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.get_nio"): + # with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.stream_pcap_file"): + # response = await compute_client.get("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) + # assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/compute/test_vmware_nodes.py b/tests/api/routes/compute/test_vmware_nodes.py index 088b4db2..ff875ab2 100644 --- a/tests/api/routes/compute/test_vmware_nodes.py +++ b/tests/api/routes/compute/test_vmware_nodes.py @@ -28,226 +28,246 @@ from gns3server.compute.project import Project pytestmark = pytest.mark.asyncio -@pytest_asyncio.fixture(scope="function") -async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vmx_path: str) -> dict: +class TestVMwareNodesRoutes: - params = { - "name": "VMTEST", - "vmx_path": vmx_path, - "linked_clone": False - } - - with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:create_vmware_node", project_id=compute_project.id), - json=params) - assert mock.called - assert response.status_code == status.HTTP_201_CREATED - return response.json() + @pytest_asyncio.fixture + async def vm(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project, vmx_path: str) -> dict: + + params = { + "name": "VMTEST", + "vmx_path": vmx_path, + "linked_clone": False + } + + with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:create_vmware_node", project_id=compute_project.id), + json=params) + assert mock.called + assert response.status_code == status.HTTP_201_CREATED + return response.json() -@pytest.fixture -def vmx_path(tmpdir: str) -> str: - """ - Return a fake VMX file - """ - - path = str(tmpdir / "test.vmx") - with open(path, 'w+') as f: - f.write("1") - return path - - -async def test_vmware_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vmx_path: str) -> None: - - params = { - "name": "VM1", - "vmx_path": vmx_path, - "linked_clone": False - } - - with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True): - response = await compute_client.post(app.url_path_for("compute:create_vmware_node", project_id=compute_project.id), - json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "VM1" + @pytest.fixture + def vmx_path(self, tmpdir: str) -> str: + """ + Return a fake VMX file + """ + + path = str(tmpdir / "test.vmx") + with open(path, 'w+') as f: + f.write("1") + return path + + + async def test_vmware_create( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vmx_path: str + ) -> None: + + params = { + "name": "VM1", + "vmx_path": vmx_path, + "linked_clone": False + } + + with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True): + response = await compute_client.post(app.url_path_for("compute:create_vmware_node", project_id=compute_project.id), + json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "VM1" + assert response.json()["project_id"] == compute_project.id + + + async def test_vmware_get( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: + + response = await compute_client.get(app.url_path_for("compute:get_vmware_node", project_id=vm["project_id"], node_id=vm["node_id"])) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "VMTEST" assert response.json()["project_id"] == compute_project.id - - -async def test_vmware_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: - - response = await compute_client.get(app.url_path_for("compute:get_vmware_node", project_id=vm["project_id"], node_id=vm["node_id"])) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "VMTEST" - assert response.json()["project_id"] == compute_project.id - - -async def test_vmware_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:start_vmware_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vmware_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:stop_vmware_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vmware_suspend(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.suspend", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:suspend_vmware_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vmware_resume(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.resume", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:resume_vmware_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vmware_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.reload", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:reload_vmware_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vmware_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for("compute:create_vmware_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_add_nio_binding') as mock: - response = await compute_client.post(url, json=params) - assert mock.called - args, kwgars = mock.call_args - assert args[0] == 0 - - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -# @pytest.mark.asyncio -# async def test_vmware_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm): -# -# params = { -# "type": "nio_udp", -# "lport": 4242, -# "rport": 4343, -# "rhost": "127.0.0.1", -# "filters": {} -# } -# -# with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM._ubridge_send'): -# with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'): -# with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock: -# response = await compute_client.put("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) -# assert response.status_code == status.HTTP_201_CREATED -# assert response.json()["type"] == "nio_udp" - - -async def test_vmware_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - url = app.url_path_for("compute:delete_vmware_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock: - response = await compute_client.delete(url) - assert mock.called - args, kwgars = mock.call_args - assert args[0] == 0 - - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vmware_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None: - - params = { - "name": "test", - "console": free_console_port - } - - response = await compute_client.put(app.url_path_for("compute:update_vmware_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json=params) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - assert response.json()["console"] == free_console_port - - -async def test_vmware_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "capture_file_name": "test.pcap", - "data_link_type": "DLT_EN10MB" - } - - url = app.url_path_for("compute:start_vmware_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start_capture") as mock: - - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_200_OK + + + async def test_vmware_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:start_vmware_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) assert mock.called - assert "test.pcap" in response.json()["pcap_file_path"] - - -async def test_vmware_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - url = app.url_path_for("compute:stop_vmware_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop_capture") as mock: - response = await compute_client.post(url) assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vmware_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:stop_vmware_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) assert mock.called - - -# @pytest.mark.asyncio -# async def test_vmware_pcap(app: FastAPI, compute_client: AsyncClient, vm, compute_project): -# -# with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.get_nio"): -# with asyncio_patch("gns3server.compute.vmware.VMware.stream_pcap_file"): -# response = await compute_client.get("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) -# assert response.status_code == status.HTTP_200_OK + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vmware_suspend(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.suspend", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:suspend_vmware_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vmware_resume(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.resume", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:resume_vmware_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vmware_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.reload", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:reload_vmware_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vmware_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + url = app.url_path_for("compute:create_vmware_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_add_nio_binding') as mock: + response = await compute_client.post(url, json=params) + assert mock.called + args, kwgars = mock.call_args + assert args[0] == 0 + + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + # @pytest.mark.asyncio + # async def test_vmware_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm): + # + # params = { + # "type": "nio_udp", + # "lport": 4242, + # "rport": 4343, + # "rhost": "127.0.0.1", + # "filters": {} + # } + # + # with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM._ubridge_send'): + # with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'): + # with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock: + # response = await compute_client.put("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) + # assert response.status_code == status.HTTP_201_CREATED + # assert response.json()["type"] == "nio_udp" + + + async def test_vmware_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + url = app.url_path_for("compute:delete_vmware_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock: + response = await compute_client.delete(url) + assert mock.called + args, kwgars = mock.call_args + assert args[0] == 0 + + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vmware_update( + self, + app: FastAPI, + compute_client: AsyncClient, + vm: dict, + free_console_port: int + ) -> None: + + params = { + "name": "test", + "console": free_console_port + } + + response = await compute_client.put(app.url_path_for("compute:update_vmware_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json=params) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + assert response.json()["console"] == free_console_port + + + async def test_vmware_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "capture_file_name": "test.pcap", + "data_link_type": "DLT_EN10MB" + } + + url = app.url_path_for("compute:start_vmware_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start_capture") as mock: + + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_200_OK + assert mock.called + assert "test.pcap" in response.json()["pcap_file_path"] + + + async def test_vmware_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + url = app.url_path_for("compute:stop_vmware_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop_capture") as mock: + response = await compute_client.post(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called + + + # @pytest.mark.asyncio + # async def test_vmware_pcap(self, app: FastAPI, compute_client: AsyncClient, vm, compute_project): + # + # with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.get_nio"): + # with asyncio_patch("gns3server.compute.vmware.VMware.stream_pcap_file"): + # response = await compute_client.get("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) + # assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/compute/test_vpcs_nodes.py b/tests/api/routes/compute/test_vpcs_nodes.py index f0b69366..0bf98781 100644 --- a/tests/api/routes/compute/test_vpcs_nodes.py +++ b/tests/api/routes/compute/test_vpcs_nodes.py @@ -28,251 +28,279 @@ from gns3server.compute.project import Project pytestmark = pytest.mark.asyncio -@pytest_asyncio.fixture -async def vm(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: +class TestVPCSNodesRoutes: - params = {"name": "PC TEST 1"} - response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED - return response.json() - - -async def test_vpcs_create(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: - - params = {"name": "PC TEST 1"} - response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "PC TEST 1" - assert response.json()["project_id"] == compute_project.id - - -async def test_vpcs_get(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: - - response = await compute_client.get(app.url_path_for("compute:get_vpcs_node", project_id=vm["project_id"], node_id=vm["node_id"])) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "PC TEST 1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["status"] == "stopped" - - -async def test_vpcs_create_startup_script(app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: - - params = { - "name": "PC TEST 1", - "startup_script": "ip 192.168.1.2\necho TEST" - } - - response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "PC TEST 1" - assert response.json()["project_id"] == compute_project.id - - -async def test_vpcs_create_port(app: FastAPI, - compute_client: AsyncClient, - compute_project: Project, - free_console_port: int) -> None: - - params = { - "name": "PC TEST 1", - "console": free_console_port - } - - response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "PC TEST 1" - assert response.json()["project_id"] == compute_project.id - assert response.json()["console"] == free_console_port - - -async def test_vpcs_nio_create_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for("compute:create_vpcs_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_vpcs_nio_update_udp(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - url = app.url_path_for("compute:create_vpcs_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): - response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - - params["filters"] = {} - url = app.url_path_for("compute:update_vpcs_node_nio", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - response = await compute_client.put(url, json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["type"] == "nio_udp" - - -async def test_vpcs_delete_nio(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1" - } - - with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._ubridge_send"): + @pytest_asyncio.fixture + async def vm(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: + + params = {"name": "PC TEST 1"} + response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED + return response.json() + + + async def test_vpcs_create(self, app: FastAPI, compute_client: AsyncClient, compute_project: Project) -> None: + + params = {"name": "PC TEST 1"} + response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "PC TEST 1" + assert response.json()["project_id"] == compute_project.id + + + async def test_vpcs_get( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: + + response = await compute_client.get(app.url_path_for("compute:get_vpcs_node", project_id=vm["project_id"], node_id=vm["node_id"])) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "PC TEST 1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["status"] == "stopped" + + + async def test_vpcs_create_startup_script( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project + ) -> None: + + params = { + "name": "PC TEST 1", + "startup_script": "ip 192.168.1.2\necho TEST" + } + + response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "PC TEST 1" + assert response.json()["project_id"] == compute_project.id + + + async def test_vpcs_create_port( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + free_console_port: int + ) -> None: + + params = { + "name": "PC TEST 1", + "console": free_console_port + } + + response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "PC TEST 1" + assert response.json()["project_id"] == compute_project.id + assert response.json()["console"] == free_console_port + + + async def test_vpcs_nio_create_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + url = app.url_path_for("compute:create_vpcs_node_nio", project_id=vm["project_id"], node_id=vm["node_id"], adapter_number="0", port_number="0") - await compute_client.post(url, json=params) - - url = app.url_path_for("compute:delete_vpcs_node_nio", + + with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + async def test_vpcs_nio_update_udp(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + url = app.url_path_for("compute:create_vpcs_node_nio", project_id=vm["project_id"], node_id=vm["node_id"], adapter_number="0", port_number="0") - response = await compute_client.delete(url) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vpcs_start(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:start_vpcs_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vpcs_stop(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop", return_value=True) as mock: - - response = await compute_client.post(app.url_path_for("compute:stop_vpcs_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vpcs_reload(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.reload", return_value=True) as mock: - response = await compute_client.post(app.url_path_for("compute:reload_vpcs_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vpcs_delete(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - with asyncio_patch("gns3server.compute.vpcs.VPCS.delete_node", return_value=True) as mock: - response = await compute_client.delete(app.url_path_for("compute:delete_vpcs_node", - project_id=vm["project_id"], - node_id=vm["node_id"])) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_vpcs_duplicate(app: FastAPI, compute_client: AsyncClient, compute_project: Project, vm: dict) -> None: - - # create destination node first - params = {"name": "PC TEST 1"} - response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED - - params = {"destination_node_id": response.json()["node_id"]} - response = await compute_client.post(app.url_path_for("compute:duplicate_vpcs_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json=params) - assert response.status_code == status.HTTP_201_CREATED - - -async def test_vpcs_update(app: FastAPI, compute_client: AsyncClient, vm: dict, free_console_port: int) -> None: - - console_port = free_console_port - params = { - "name": "test", - "console": console_port - } - - response = await compute_client.put(app.url_path_for("compute:update_vpcs_node", - project_id=vm["project_id"], - node_id=vm["node_id"]), json=params) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - assert response.json()["console"] == console_port - - -async def test_vpcs_start_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - params = { - "capture_file_name": "test.pcap", - "data_link_type": "DLT_EN10MB" - } - - url = app.url_path_for("compute:start_vpcs_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_capture") as mock: + + with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): response = await compute_client.post(url, json=params) - assert response.status_code == status.HTTP_200_OK + assert response.status_code == status.HTTP_201_CREATED + + params["filters"] = {} + url = app.url_path_for("compute:update_vpcs_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + response = await compute_client.put(url, json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["type"] == "nio_udp" + + + async def test_vpcs_delete_nio(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "type": "nio_udp", + "lport": 4242, + "rport": 4343, + "rhost": "127.0.0.1" + } + + with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._ubridge_send"): + url = app.url_path_for("compute:create_vpcs_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + await compute_client.post(url, json=params) + + url = app.url_path_for("compute:delete_vpcs_node_nio", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + response = await compute_client.delete(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vpcs_start(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:start_vpcs_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) assert mock.called - assert "test.pcap" in response.json()["pcap_file_path"] - - -async def test_vpcs_stop_capture(app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: - - url = app.url_path_for("compute:stop_vpcs_node_capture", - project_id=vm["project_id"], - node_id=vm["node_id"], - adapter_number="0", - port_number="0") - - with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True): - with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop_capture") as mock: - response = await compute_client.post(url) assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vpcs_stop(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop", return_value=True) as mock: + + response = await compute_client.post(app.url_path_for("compute:stop_vpcs_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) assert mock.called - - -# @pytest.mark.asyncio -# async def test_vpcs_pcap(app: FastAPI, compute_client: AsyncClient, vm, compute_project: Project): -# -# with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.get_nio"): -# with asyncio_patch("gns3server.compute.vpcs.VPCS.stream_pcap_file"): -# response = await compute_client.get("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) -# assert response.status_code == status.HTTP_200_OK + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vpcs_reload(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.reload", return_value=True) as mock: + response = await compute_client.post(app.url_path_for("compute:reload_vpcs_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vpcs_delete(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + with asyncio_patch("gns3server.compute.vpcs.VPCS.delete_node", return_value=True) as mock: + response = await compute_client.delete(app.url_path_for("compute:delete_vpcs_node", + project_id=vm["project_id"], + node_id=vm["node_id"])) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_vpcs_duplicate( + self, + app: FastAPI, + compute_client: AsyncClient, + compute_project: Project, + vm: dict + ) -> None: + + # create destination node first + params = {"name": "PC TEST 1"} + response = await compute_client.post(app.url_path_for("compute:create_vpcs_node", project_id=compute_project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED + + params = {"destination_node_id": response.json()["node_id"]} + response = await compute_client.post(app.url_path_for("compute:duplicate_vpcs_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json=params) + assert response.status_code == status.HTTP_201_CREATED + + + async def test_vpcs_update( + self, + app: FastAPI, + compute_client: AsyncClient, + vm: dict, + free_console_port: int + ) -> None: + + console_port = free_console_port + params = { + "name": "test", + "console": console_port + } + + response = await compute_client.put(app.url_path_for("compute:update_vpcs_node", + project_id=vm["project_id"], + node_id=vm["node_id"]), json=params) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + assert response.json()["console"] == console_port + + + async def test_vpcs_start_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + params = { + "capture_file_name": "test.pcap", + "data_link_type": "DLT_EN10MB" + } + + url = app.url_path_for("compute:start_vpcs_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_capture") as mock: + response = await compute_client.post(url, json=params) + assert response.status_code == status.HTTP_200_OK + assert mock.called + assert "test.pcap" in response.json()["pcap_file_path"] + + + async def test_vpcs_stop_capture(self, app: FastAPI, compute_client: AsyncClient, vm: dict) -> None: + + url = app.url_path_for("compute:stop_vpcs_node_capture", + project_id=vm["project_id"], + node_id=vm["node_id"], + adapter_number="0", + port_number="0") + + with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True): + with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop_capture") as mock: + response = await compute_client.post(url) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called + + + # @pytest.mark.asyncio + # async def test_vpcs_pcap(self, app: FastAPI, compute_client: AsyncClient, vm, compute_project: Project): + # + # with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.get_nio"): + # with asyncio_patch("gns3server.compute.vpcs.VPCS.stream_pcap_file"): + # response = await compute_client.get("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) + # assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/controller/test_controller.py b/tests/api/routes/controller/test_controller.py index 50c64c73..ddd33aa8 100644 --- a/tests/api/routes/controller/test_controller.py +++ b/tests/api/routes/controller/test_controller.py @@ -27,43 +27,45 @@ from gns3server.config import Config pytestmark = pytest.mark.asyncio -async def test_shutdown_local(app: FastAPI, client: AsyncClient, config: Config) -> None: +class TestControllerRoutes: - os.kill = MagicMock() - config.settings.Server.local = True - response = await client.post(app.url_path_for("shutdown")) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert os.kill.called - - -async def test_shutdown_non_local(app: FastAPI, client: AsyncClient, config: Config) -> None: - - response = await client.post(app.url_path_for("shutdown")) - assert response.status_code == status.HTTP_403_FORBIDDEN - - -# @pytest.mark.asyncio -# async def test_debug(controller_api, config, tmpdir): -# -# config._main_config_file = str(tmpdir / "test.conf") -# config.set("Server", "local", True) -# response = await controller_api.post('/debug') -# assert response.status_code == 201 -# debug_dir = os.path.join(config.config_dir, "debug") -# assert os.path.exists(debug_dir) -# assert os.path.exists(os.path.join(debug_dir, "controller.txt")) -# -# -# @pytest.mark.asyncio -# async def test_debug_non_local(controller_api, config, tmpdir): -# -# config._main_config_file = str(tmpdir / "test.conf") -# config.set("Server", "local", False) -# response = await controller_api.post('/debug') -# assert response.status_code == 403 - - -async def test_statistics_output(app: FastAPI, client: AsyncClient) -> None: - - response = await client.get(app.url_path_for("statistics")) - assert response.status_code == status.HTTP_200_OK + async def test_shutdown_local(self, app: FastAPI, client: AsyncClient, config: Config) -> None: + + os.kill = MagicMock() + config.settings.Server.local = True + response = await client.post(app.url_path_for("shutdown")) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert os.kill.called + + + async def test_shutdown_non_local(self, app: FastAPI, client: AsyncClient, config: Config) -> None: + + response = await client.post(app.url_path_for("shutdown")) + assert response.status_code == status.HTTP_403_FORBIDDEN + + + # @pytest.mark.asyncio + # async def test_debug(controller_api, config, tmpdir): + # + # config._main_config_file = str(tmpdir / "test.conf") + # config.set("Server", "local", True) + # response = await controller_api.post('/debug') + # assert response.status_code == 201 + # debug_dir = os.path.join(config.config_dir, "debug") + # assert os.path.exists(debug_dir) + # assert os.path.exists(os.path.join(debug_dir, "controller.txt")) + # + # + # @pytest.mark.asyncio + # async def test_debug_non_local(controller_api, config, tmpdir): + # + # config._main_config_file = str(tmpdir / "test.conf") + # config.set("Server", "local", False) + # response = await controller_api.post('/debug') + # assert response.status_code == 403 + + + async def test_statistics_output(self, app: FastAPI, client: AsyncClient) -> None: + + response = await client.get(app.url_path_for("statistics")) + assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/controller/test_drawings.py b/tests/api/routes/controller/test_drawings.py index c5fcec38..62f00d5f 100644 --- a/tests/api/routes/controller/test_drawings.py +++ b/tests/api/routes/controller/test_drawings.py @@ -26,89 +26,91 @@ from gns3server.controller.project import Project pytestmark = pytest.mark.asyncio -async def test_create_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None: +class TestDrawingsRoutes: - params = { - "svg": '', - "x": 10, - "y": 20, - "z": 0 - } - - response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["drawing_id"] is not None - - -async def test_get_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None: - - params = { - "svg": '', - "x": 10, - "y": 20, - "z": 0 - } - - response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) - response = await client.get(app.url_path_for( - "get_drawing", - project_id=project.id, - drawing_id=response.json()["drawing_id"]) - ) - assert response.status_code == 200 - assert response.json()["x"] == 10 - - -async def test_update_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None: - - params = { - "svg": '', - "x": 10, - "y": 20, - "z": 0 - } - - response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) - response = await client.put(app.url_path_for( - "update_drawing", - project_id=project.id, - drawing_id=response.json()["drawing_id"]), - json={"x": 42} - ) - - assert response.status_code == status.HTTP_200_OK - assert response.json()["x"] == 42 - - -async def test_all_drawings(app: FastAPI, client: AsyncClient, project: Project) -> None: - - params = { - "svg": '', - "x": 10, - "y": 20, - "z": 0 - } - - await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) - response = await client.get(app.url_path_for("get_drawings", project_id=project.id)) - assert response.status_code == status.HTTP_200_OK - assert len(response.json()) == 1 - - # test listing links from a closed project - await project.close(ignore_notification=True) - response = await client.get(app.url_path_for("get_drawings", project_id=project.id)) - assert response.status_code == status.HTTP_200_OK - assert len(response.json()) == 1 - - -async def test_delete_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None: - - drawing = Drawing(project) - project._drawings = {drawing.id: drawing} - response = await client.delete(app.url_path_for( - "delete_drawing", - project_id=project.id, - drawing_id=drawing.id) - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert drawing.id not in project.drawings + async def test_create_drawing(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + params = { + "svg": '', + "x": 10, + "y": 20, + "z": 0 + } + + response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["drawing_id"] is not None + + + async def test_get_drawing(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + params = { + "svg": '', + "x": 10, + "y": 20, + "z": 0 + } + + response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) + response = await client.get(app.url_path_for( + "get_drawing", + project_id=project.id, + drawing_id=response.json()["drawing_id"]) + ) + assert response.status_code == 200 + assert response.json()["x"] == 10 + + + async def test_update_drawing(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + params = { + "svg": '', + "x": 10, + "y": 20, + "z": 0 + } + + response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) + response = await client.put(app.url_path_for( + "update_drawing", + project_id=project.id, + drawing_id=response.json()["drawing_id"]), + json={"x": 42} + ) + + assert response.status_code == status.HTTP_200_OK + assert response.json()["x"] == 42 + + + async def test_all_drawings(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + params = { + "svg": '', + "x": 10, + "y": 20, + "z": 0 + } + + await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) + response = await client.get(app.url_path_for("get_drawings", project_id=project.id)) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()) == 1 + + # test listing links from a closed project + await project.close(ignore_notification=True) + response = await client.get(app.url_path_for("get_drawings", project_id=project.id)) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()) == 1 + + + async def test_delete_drawing(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + drawing = Drawing(project) + project._drawings = {drawing.id: drawing} + response = await client.delete(app.url_path_for( + "delete_drawing", + project_id=project.id, + drawing_id=drawing.id) + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert drawing.id not in project.drawings diff --git a/tests/api/routes/controller/test_gns3vm.py b/tests/api/routes/controller/test_gns3vm.py index bb41f1ce..ce10bfb8 100644 --- a/tests/api/routes/controller/test_gns3vm.py +++ b/tests/api/routes/controller/test_gns3vm.py @@ -24,32 +24,34 @@ from tests.utils import asyncio_patch pytestmark = pytest.mark.asyncio -async def test_list_vms(app: FastAPI, client: AsyncClient) -> None: +class TestGNS3VMRoutes: - with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test"}]): - response = await client.get(app.url_path_for("get_vms", engine="vmware")) - assert response.status_code == status.HTTP_200_OK - assert response.json() == [ - { - "vmname": "test" - } - ] - - -async def test_engines(app: FastAPI, client: AsyncClient) -> None: - - response = await client.get(app.url_path_for("get_engines")) - assert response.status_code == status.HTTP_200_OK - assert len(response.json()) > 0 - - -async def test_put_gns3vm(app: FastAPI, client: AsyncClient) -> None: - - response = await client.put(app.url_path_for("update_gns3vm_settings"), json={"vmname": "TEST VM"}) - assert response.status_code == status.HTTP_200_OK - assert response.json()["vmname"] == "TEST VM" - - -async def test_get_gns3vm(app: FastAPI, client: AsyncClient) -> None: - response = await client.get(app.url_path_for("get_gns3vm_settings")) - assert response.status_code == status.HTTP_200_OK + async def test_list_vms(self, app: FastAPI, client: AsyncClient) -> None: + + with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test"}]): + response = await client.get(app.url_path_for("get_vms", engine="vmware")) + assert response.status_code == status.HTTP_200_OK + assert response.json() == [ + { + "vmname": "test" + } + ] + + + async def test_engines(self, app: FastAPI, client: AsyncClient) -> None: + + response = await client.get(app.url_path_for("get_engines")) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()) > 0 + + + async def test_put_gns3vm(self, app: FastAPI, client: AsyncClient) -> None: + + response = await client.put(app.url_path_for("update_gns3vm_settings"), json={"vmname": "TEST VM"}) + assert response.status_code == status.HTTP_200_OK + assert response.json()["vmname"] == "TEST VM" + + + async def test_get_gns3vm(self, app: FastAPI, client: AsyncClient) -> None: + response = await client.get(app.url_path_for("get_gns3vm_settings")) + assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/controller/test_links.py b/tests/api/routes/controller/test_links.py index ab6bf476..2103ddaa 100644 --- a/tests/api/routes/controller/test_links.py +++ b/tests/api/routes/controller/test_links.py @@ -35,30 +35,88 @@ from gns3server.controller.udp_link import UDPLink pytestmark = pytest.mark.asyncio -@pytest_asyncio.fixture -async def nodes(compute: Compute, project: Project) -> Tuple[Node, Node]: +class TestLinkRoutes: - response = MagicMock() - response.json = {"console": 2048} - compute.post = AsyncioMagicMock(return_value=response) - - node1 = await project.add_node(compute, "node1", None, node_type="qemu") - node1._ports = [EthernetPort("E0", 0, 0, 3)] - node2 = await project.add_node(compute, "node2", None, node_type="qemu") - node2._ports = [EthernetPort("E0", 0, 2, 4)] - return node1, node2 - - -async def test_create_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None: - - node1, node2 = nodes - - filters = { - "latency": [10], - "frequency_drop": [50] - } - - with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: + @pytest_asyncio.fixture + async def nodes(self, compute: Compute, project: Project) -> Tuple[Node, Node]: + + response = MagicMock() + response.json = {"console": 2048} + compute.post = AsyncioMagicMock(return_value=response) + + node1 = await project.add_node(compute, "node1", None, node_type="qemu") + node1._ports = [EthernetPort("E0", 0, 0, 3)] + node2 = await project.add_node(compute, "node2", None, node_type="qemu") + node2._ports = [EthernetPort("E0", 0, 2, 4)] + return node1, node2 + + + async def test_create_link( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + nodes: Tuple[Node, Node] + ) -> None: + + node1, node2 = nodes + + filters = { + "latency": [10], + "frequency_drop": [50] + } + + with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: + response = await client.post(app.url_path_for("create_link", project_id=project.id), json={ + "nodes": [ + { + "node_id": node1.id, + "adapter_number": 0, + "port_number": 3, + "label": { + "text": "Text", + "x": 42, + "y": 0 + } + }, + { + "node_id": node2.id, + "adapter_number": 2, + "port_number": 4 + } + ], + "filters": filters + }) + + assert mock.called + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["link_id"] is not None + assert len(response.json()["nodes"]) == 2 + assert response.json()["nodes"][0]["label"]["x"] == 42 + assert len(project.links) == 1 + assert list(project.links.values())[0].filters == filters + + + async def test_create_link_failure( + self, + app: FastAPI, + client: AsyncClient, + compute: Compute, + project: Project + ) -> None: + """ + Make sure the link is deleted if we failed to create it. + + The failure is triggered by connecting the link to itself + """ + + response = MagicMock() + response.json = {"console": 2048} + compute.post = AsyncioMagicMock(return_value=response) + + node1 = await project.add_node(compute, "node1", None, node_type="qemu") + node1._ports = [EthernetPort("E0", 0, 0, 3), EthernetPort("E0", 0, 0, 4)] + response = await client.post(app.url_path_for("create_link", project_id=project.id), json={ "nodes": [ { @@ -71,6 +129,168 @@ async def test_create_link(app: FastAPI, client: AsyncClient, project: Project, "y": 0 } }, + { + "node_id": node1.id, + "adapter_number": 0, + "port_number": 4 + } + ] + }) + + assert response.status_code == status.HTTP_409_CONFLICT + assert len(project.links) == 0 + + + async def test_get_link( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + nodes: Tuple[Node, Node] + ) -> None: + + node1, node2 = nodes + with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: + response = await client.post(app.url_path_for("create_link", project_id=project.id), json={ + "nodes": [ + { + "node_id": node1.id, + "adapter_number": 0, + "port_number": 3, + "label": { + "text": "Text", + "x": 42, + "y": 0 + } + }, + { + "node_id": node2.id, + "adapter_number": 2, + "port_number": 4 + } + ] + }) + + assert mock.called + link_id = response.json()["link_id"] + assert response.json()["nodes"][0]["label"]["x"] == 42 + response = await client.get(app.url_path_for("get_link", project_id=project.id, link_id=link_id)) + assert response.status_code == status.HTTP_200_OK + assert response.json()["nodes"][0]["label"]["x"] == 42 + + + async def test_update_link_suspend( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + nodes: Tuple[Node, Node] + ) -> None: + + node1, node2 = nodes + with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: + response = await client.post(app.url_path_for("create_link", project_id=project.id), json={ + "nodes": [ + { + "node_id": node1.id, + "adapter_number": 0, + "port_number": 3, + "label": { + "text": "Text", + "x": 42, + "y": 0 + } + }, + { + "node_id": node2.id, + "adapter_number": 2, + "port_number": 4 + } + ] + }) + + assert mock.called + link_id = response.json()["link_id"] + assert response.json()["nodes"][0]["label"]["x"] == 42 + + response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={ + "nodes": [ + { + "node_id": node1.id, + "adapter_number": 0, + "port_number": 3, + "label": { + "text": "Hello", + "x": 64, + "y": 0 + } + }, + { + "node_id": node2.id, + "adapter_number": 2, + "port_number": 4 + } + ], + "suspend": True + }) + + assert response.status_code == status.HTTP_200_OK + assert response.json()["nodes"][0]["label"]["x"] == 64 + assert response.json()["suspend"] + assert response.json()["filters"] == {} + + + async def test_update_link( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + nodes: Tuple[Node, Node] + ) -> None: + + filters = { + "latency": [10], + "frequency_drop": [50] + } + + node1, node2 = nodes + with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: + response = await client.post(app.url_path_for("create_link", project_id=project.id), json={ + "nodes": [ + { + "node_id": node1.id, + "adapter_number": 0, + "port_number": 3, + "label": { + "text": "Text", + "x": 42, + "y": 0 + } + }, + { + "node_id": node2.id, + "adapter_number": 2, + "port_number": 4 + } + ] + }) + + assert mock.called + link_id = response.json()["link_id"] + assert response.json()["nodes"][0]["label"]["x"] == 42 + + response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={ + "nodes": [ + { + "node_id": node1.id, + "adapter_number": 0, + "port_number": 3, + "label": { + "text": "Hello", + "x": 64, + "y": 0 + } + }, { "node_id": node2.id, "adapter_number": 2, @@ -79,309 +299,127 @@ async def test_create_link(app: FastAPI, client: AsyncClient, project: Project, ], "filters": filters }) - - assert mock.called - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["link_id"] is not None - assert len(response.json()["nodes"]) == 2 - assert response.json()["nodes"][0]["label"]["x"] == 42 - assert len(project.links) == 1 - assert list(project.links.values())[0].filters == filters - - -async def test_create_link_failure(app: FastAPI, client: AsyncClient, compute: Compute, project: Project) -> None: - """ - Make sure the link is deleted if we failed to create it. - - The failure is triggered by connecting the link to itself - """ - - response = MagicMock() - response.json = {"console": 2048} - compute.post = AsyncioMagicMock(return_value=response) - - node1 = await project.add_node(compute, "node1", None, node_type="qemu") - node1._ports = [EthernetPort("E0", 0, 0, 3), EthernetPort("E0", 0, 0, 4)] - - response = await client.post(app.url_path_for("create_link", project_id=project.id), json={ - "nodes": [ + + assert response.status_code == status.HTTP_200_OK + assert response.json()["nodes"][0]["label"]["x"] == 64 + assert list(project.links.values())[0].filters == filters + + + async def test_list_link( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + nodes: Tuple[Node, Node] + ) -> None: + + filters = { + "latency": [10], + "frequency_drop": [50] + } + + node1, node2 = nodes + nodes = [ { "node_id": node1.id, "adapter_number": 0, - "port_number": 3, - "label": { - "text": "Text", - "x": 42, - "y": 0 - } + "port_number": 3 }, { - "node_id": node1.id, - "adapter_number": 0, + "node_id": node2.id, + "adapter_number": 2, "port_number": 4 } ] - }) - - assert response.status_code == status.HTTP_409_CONFLICT - assert len(project.links) == 0 - - -async def test_get_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None: - - node1, node2 = nodes - with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: - response = await client.post(app.url_path_for("create_link", project_id=project.id), json={ - "nodes": [ - { - "node_id": node1.id, - "adapter_number": 0, - "port_number": 3, - "label": { - "text": "Text", - "x": 42, - "y": 0 - } - }, - { - "node_id": node2.id, - "adapter_number": 2, - "port_number": 4 - } - ] - }) - - assert mock.called - link_id = response.json()["link_id"] - assert response.json()["nodes"][0]["label"]["x"] == 42 - response = await client.get(app.url_path_for("get_link", project_id=project.id, link_id=link_id)) - assert response.status_code == status.HTTP_200_OK - assert response.json()["nodes"][0]["label"]["x"] == 42 - - -async def test_update_link_suspend(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None: - - node1, node2 = nodes - with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: - response = await client.post(app.url_path_for("create_link", project_id=project.id), json={ - "nodes": [ - { - "node_id": node1.id, - "adapter_number": 0, - "port_number": 3, - "label": { - "text": "Text", - "x": 42, - "y": 0 - } - }, - { - "node_id": node2.id, - "adapter_number": 2, - "port_number": 4 - } - ] - }) - - assert mock.called - link_id = response.json()["link_id"] - assert response.json()["nodes"][0]["label"]["x"] == 42 - - response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={ - "nodes": [ - { - "node_id": node1.id, - "adapter_number": 0, - "port_number": 3, - "label": { - "text": "Hello", - "x": 64, - "y": 0 - } - }, - { - "node_id": node2.id, - "adapter_number": 2, - "port_number": 4 - } - ], - "suspend": True - }) - - assert response.status_code == status.HTTP_200_OK - assert response.json()["nodes"][0]["label"]["x"] == 64 - assert response.json()["suspend"] - assert response.json()["filters"] == {} - - -async def test_update_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None: - - filters = { - "latency": [10], - "frequency_drop": [50] - } - - node1, node2 = nodes - with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: - response = await client.post(app.url_path_for("create_link", project_id=project.id), json={ - "nodes": [ - { - "node_id": node1.id, - "adapter_number": 0, - "port_number": 3, - "label": { - "text": "Text", - "x": 42, - "y": 0 - } - }, - { - "node_id": node2.id, - "adapter_number": 2, - "port_number": 4 - } - ] - }) - - assert mock.called - link_id = response.json()["link_id"] - assert response.json()["nodes"][0]["label"]["x"] == 42 - - response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={ - "nodes": [ - { - "node_id": node1.id, - "adapter_number": 0, - "port_number": 3, - "label": { - "text": "Hello", - "x": 64, - "y": 0 - } - }, - { - "node_id": node2.id, - "adapter_number": 2, - "port_number": 4 - } - ], - "filters": filters - }) - - assert response.status_code == status.HTTP_200_OK - assert response.json()["nodes"][0]["label"]["x"] == 64 - assert list(project.links.values())[0].filters == filters - - -async def test_list_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None: - - filters = { - "latency": [10], - "frequency_drop": [50] - } - - node1, node2 = nodes - nodes = [ - { - "node_id": node1.id, - "adapter_number": 0, - "port_number": 3 - }, - { - "node_id": node2.id, - "adapter_number": 2, - "port_number": 4 - } - ] - with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: - await client.post(app.url_path_for("create_link", project_id=project.id), json={ - "nodes": nodes, - "filters": filters - }) - - assert mock.called - response = await client.get(app.url_path_for("get_links", project_id=project.id)) - assert response.status_code == status.HTTP_200_OK - assert len(response.json()) == 1 - assert response.json()[0]["filters"] == filters - - # test listing links from a closed project - await project.close(ignore_notification=True) - response = await client.get(app.url_path_for("get_links", project_id=project.id)) - assert response.status_code == status.HTTP_200_OK - assert len(response.json()) == 1 - assert response.json()[0]["filters"] == filters - - -async def test_reset_link(app: FastAPI, client: AsyncClient, project: Project) -> None: - - link = UDPLink(project) - project._links = {link.id: link} - with asyncio_patch("gns3server.controller.udp_link.UDPLink.delete") as delete_mock: - with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as create_mock: - response = await client.post(app.url_path_for("reset_link", project_id=project.id, link_id=link.id)) - assert delete_mock.called - assert create_mock.called - assert response.status_code == status.HTTP_200_OK - - -async def test_start_capture(app: FastAPI, client: AsyncClient, project: Project) -> None: - - link = Link(project) - project._links = {link.id: link} - with asyncio_patch("gns3server.controller.link.Link.start_capture") as mock: - response = await client.post(app.url_path_for("start_capture", project_id=project.id, link_id=link.id), json={}) + with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: + await client.post(app.url_path_for("create_link", project_id=project.id), json={ + "nodes": nodes, + "filters": filters + }) + assert mock.called - assert response.status_code == status.HTTP_201_CREATED - - -async def test_stop_capture(app: FastAPI, client: AsyncClient, project: Project) -> None: - - link = Link(project) - project._links = {link.id: link} - with asyncio_patch("gns3server.controller.link.Link.stop_capture") as mock: - response = await client.post(app.url_path_for("stop_capture", project_id=project.id, link_id=link.id)) + response = await client.get(app.url_path_for("get_links", project_id=project.id)) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()) == 1 + assert response.json()[0]["filters"] == filters + + # test listing links from a closed project + await project.close(ignore_notification=True) + response = await client.get(app.url_path_for("get_links", project_id=project.id)) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()) == 1 + assert response.json()[0]["filters"] == filters + + + async def test_reset_link(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + link = UDPLink(project) + project._links = {link.id: link} + with asyncio_patch("gns3server.controller.udp_link.UDPLink.delete") as delete_mock: + with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as create_mock: + response = await client.post(app.url_path_for("reset_link", project_id=project.id, link_id=link.id)) + assert delete_mock.called + assert create_mock.called + assert response.status_code == status.HTTP_200_OK + + + async def test_start_capture(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + link = Link(project) + project._links = {link.id: link} + with asyncio_patch("gns3server.controller.link.Link.start_capture") as mock: + response = await client.post(app.url_path_for("start_capture", project_id=project.id, link_id=link.id), json={}) + assert mock.called + assert response.status_code == status.HTTP_201_CREATED + + + async def test_stop_capture(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + link = Link(project) + project._links = {link.id: link} + with asyncio_patch("gns3server.controller.link.Link.stop_capture") as mock: + response = await client.post(app.url_path_for("stop_capture", project_id=project.id, link_id=link.id)) + assert mock.called + assert response.status_code == status.HTTP_204_NO_CONTENT + + + # async def test_pcap(controller_api, http_client, project): + # + # async def pcap_capture(): + # async with http_client.get(controller_api.get_url("/projects/{}/links/{}/pcap".format(project.id, link.id))) as response: + # response.body = await response.content.read(5) + # print("READ", response.body) + # return response + # + # with asyncio_patch("gns3server.controller.link.Link.capture_node") as mock: + # link = Link(project) + # link._capture_file_name = "test" + # link._capturing = True + # with open(link.capture_file_path, "w+") as f: + # f.write("hello") + # project._links = {link.id: link} + # response = await pcap_capture() + # assert mock.called + # assert response.status_code == 200 + # assert b'hello' == response.body + + + async def test_delete_link(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + link = Link(project) + project._links = {link.id: link} + with asyncio_patch("gns3server.controller.link.Link.delete") as mock: + response = await client.delete(app.url_path_for("delete_link", project_id=project.id, link_id=link.id)) assert mock.called assert response.status_code == status.HTTP_204_NO_CONTENT - - -# async def test_pcap(controller_api, http_client, project): -# -# async def pcap_capture(): -# async with http_client.get(controller_api.get_url("/projects/{}/links/{}/pcap".format(project.id, link.id))) as response: -# response.body = await response.content.read(5) -# print("READ", response.body) -# return response -# -# with asyncio_patch("gns3server.controller.link.Link.capture_node") as mock: -# link = Link(project) -# link._capture_file_name = "test" -# link._capturing = True -# with open(link.capture_file_path, "w+") as f: -# f.write("hello") -# project._links = {link.id: link} -# response = await pcap_capture() -# assert mock.called -# assert response.status_code == 200 -# assert b'hello' == response.body - - -async def test_delete_link(app: FastAPI, client: AsyncClient, project: Project) -> None: - - link = Link(project) - project._links = {link.id: link} - with asyncio_patch("gns3server.controller.link.Link.delete") as mock: - response = await client.delete(app.url_path_for("delete_link", project_id=project.id, link_id=link.id)) - assert mock.called - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_list_filters(app: FastAPI, client: AsyncClient, project: Project) -> None: - - link = Link(project) - project._links = {link.id: link} - with patch("gns3server.controller.link.Link.available_filters", return_value=FILTERS) as mock: - response = await client.get(app.url_path_for("get_filters", project_id=project.id, link_id=link.id)) - assert mock.called - assert response.status_code == status.HTTP_200_OK - assert response.json() == FILTERS + + + async def test_list_filters(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + link = Link(project) + project._links = {link.id: link} + with patch("gns3server.controller.link.Link.available_filters", return_value=FILTERS) as mock: + response = await client.get(app.url_path_for("get_filters", project_id=project.id, link_id=link.id)) + assert mock.called + assert response.status_code == status.HTTP_200_OK + assert response.json() == FILTERS diff --git a/tests/api/routes/controller/test_nodes.py b/tests/api/routes/controller/test_nodes.py index 534fe136..46f42e4a 100644 --- a/tests/api/routes/controller/test_nodes.py +++ b/tests/api/routes/controller/test_nodes.py @@ -31,416 +31,537 @@ from gns3server.controller.compute import Compute pytestmark = pytest.mark.asyncio -@pytest.fixture -def node(project: Project, compute: Compute) -> Node: - - node = Node(project, compute, "test", node_type="vpcs") - project._nodes[node.id] = node - return node - - -async def test_create_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: - - response = MagicMock() - response.json = {"console": 2048} - compute.post = AsyncioMagicMock(return_value=response) - - response = await client.post(app.url_path_for("create_node", project_id=project.id), json={ - "name": "test", - "node_type": "vpcs", - "compute_id": "example.com", - "properties": { - "startup_script": "echo test" - } - }) - - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "test" - assert "name" not in response.json()["properties"] - - -async def test_list_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: - - response = MagicMock() - response.json = {"console": 2048} - compute.post = AsyncioMagicMock(return_value=response) - - await client.post(app.url_path_for("create_node", project_id=project.id), json={ - "name": "test", - "node_type": "vpcs", - "compute_id": "example.com", - "properties": { - "startup_script": "echo test" - } - }) - - response = await client.get(app.url_path_for("get_nodes", project_id=project.id)) - assert response.status_code == status.HTTP_200_OK - assert response.json()[0]["name"] == "test" - - # test listing nodes from a closed project - await project.close(ignore_notification=True) - response = await client.get(app.url_path_for("get_nodes", project_id=project.id)) - assert response.status_code == status.HTTP_200_OK - assert response.json()[0]["name"] == "test" - - -async def test_get_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: - - response = MagicMock() - response.json = {"console": 2048} - compute.post = AsyncioMagicMock(return_value=response) - - response = await client.post(app.url_path_for("create_node", project_id=project.id), json={ - "name": "test", - "node_type": "vpcs", - "compute_id": "example.com", - "properties": { - "startup_script": "echo test" - } - }) - - response = await client.get(app.url_path_for("get_node", project_id=project.id, node_id=response.json()["node_id"])) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - - -async def test_update_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None: - - response = MagicMock() - response.json = {"console": 2048} - compute.put = AsyncioMagicMock(return_value=response) - - response = await client.put(app.url_path_for("update_node", project_id=project.id, node_id=node.id), json={ - "name": "test", - "node_type": "vpcs", - "compute_id": "example.com", - "properties": { - "startup_script": "echo test" - } - }) - - assert response.status_code == 200 - assert response.json()["name"] == "test" - assert "name" not in response.json()["properties"] - - -async def test_start_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("start_all_nodes", project_id=project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_stop_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("stop_all_nodes", project_id=project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_suspend_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("suspend_all_nodes", project_id=project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_reload_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("reload_all_nodes", project_id=project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_reset_console_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("reset_console_all_nodes", project_id=project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_start_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None: - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("start_node", project_id=project.id, node_id=node.id), json={}) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_stop_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None: - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("stop_node", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - -async def test_suspend_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None: - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("suspend_node", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_reload_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node): - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("reload_node", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_isolate_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node): - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("isolate_node", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_unisolate_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node): - - compute.post = AsyncioMagicMock() - response = await client.post(app.url_path_for("unisolate_node", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_duplicate_node( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node) -> None: - - response = MagicMock() - response.json({"console": 2035}) - compute.post = AsyncioMagicMock(return_value=response) - - response = await client.post(app.url_path_for("duplicate_node", project_id=project.id, node_id=node.id), - json={"x": 10, "y": 5, "z": 0}) - assert response.status_code == status.HTTP_201_CREATED - - -async def test_delete_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None: - - compute.post = AsyncioMagicMock() - response = await client.delete(app.url_path_for("delete_node", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_dynamips_idle_pc( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = MagicMock() - response.json = {"idlepc": "0x60606f54"} - compute.get = AsyncioMagicMock(return_value=response) - - node._node_type = "dynamips" # force Dynamips node type - response = await client.get(app.url_path_for("auto_idlepc", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_200_OK - assert response.json()["idlepc"] == "0x60606f54" - - -async def test_dynamips_idle_pc_wrong_node_type( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = await client.get(app.url_path_for("auto_idlepc", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_400_BAD_REQUEST - - -async def test_dynamips_idlepc_proposals( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = MagicMock() - response.json = ["0x60606f54", "0x33805a22"] - compute.get = AsyncioMagicMock(return_value=response) - - node._node_type = "dynamips" # force Dynamips node type - response = await client.get(app.url_path_for("idlepc_proposals", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_200_OK - assert response.json() == ["0x60606f54", "0x33805a22"] - - -async def test_dynamips_idlepc_proposals_wrong_node_type( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = await client.get(app.url_path_for("idlepc_proposals", project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_400_BAD_REQUEST - - -async def test_qemu_disk_image_create( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = MagicMock() - compute.post = AsyncioMagicMock(return_value=response) - - node._node_type = "qemu" # force Qemu node type - response = await client.post( - app.url_path_for("create_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"), - json={"format": "qcow2", "size": 30} - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_disk_image_create_wrong_node_type( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = await client.post( - app.url_path_for("create_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"), - json={"format": "qcow2", "size": 30} - ) - assert response.status_code == status.HTTP_400_BAD_REQUEST - - -async def test_qemu_disk_image_update( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = MagicMock() - compute.put = AsyncioMagicMock(return_value=response) - - node._node_type = "qemu" # force Qemu node type - response = await client.put( - app.url_path_for("update_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"), - json={"extend": 10} - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_disk_image_update_wrong_node_type( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = await client.put( - app.url_path_for("update_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"), - json={"extend": 10} - ) - assert response.status_code == status.HTTP_400_BAD_REQUEST - - -async def test_qemu_disk_image_delete( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = MagicMock() - compute.delete = AsyncioMagicMock(return_value=response) - - node._node_type = "qemu" # force Qemu node type - response = await client.delete( - app.url_path_for("delete_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2") - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -async def test_qemu_disk_image_delete_wrong_node_type( - app: FastAPI, - client: AsyncClient, - project: Project, - compute: Compute, - node: Node -) -> None: - - response = await client.delete( - app.url_path_for("delete_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2") - ) - assert response.status_code == status.HTTP_400_BAD_REQUEST - - -async def test_get_file(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None: - - response = MagicMock() - response.body = b"world" - response.status = status.HTTP_200_OK - compute.http_query = AsyncioMagicMock(return_value=response) - - response = await client.get(app.url_path_for("get_file", project_id=project.id, node_id=node.id, file_path="hello")) - assert response.status_code == status.HTTP_200_OK - assert response.content == b'world' - - compute.http_query.assert_called_with( - "GET", - "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format( +class TestNodeRoutes: + + + @pytest.fixture + def node(self, project: Project, compute: Compute) -> Node: + + node = Node(project, compute, "test", node_type="vpcs") + project._nodes[node.id] = node + return node + + + async def test_create_node(self, app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: + + response = MagicMock() + response.json = {"console": 2048} + compute.post = AsyncioMagicMock(return_value=response) + + response = await client.post(app.url_path_for("create_node", project_id=project.id), json={ + "name": "test", + "node_type": "vpcs", + "compute_id": "example.com", + "properties": { + "startup_script": "echo test" + } + }) + + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "test" + assert "name" not in response.json()["properties"] + + + async def test_list_node(self, app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: + + response = MagicMock() + response.json = {"console": 2048} + compute.post = AsyncioMagicMock(return_value=response) + + await client.post(app.url_path_for("create_node", project_id=project.id), json={ + "name": "test", + "node_type": "vpcs", + "compute_id": "example.com", + "properties": { + "startup_script": "echo test" + } + }) + + response = await client.get(app.url_path_for("get_nodes", project_id=project.id)) + assert response.status_code == status.HTTP_200_OK + assert response.json()[0]["name"] == "test" + + # test listing nodes from a closed project + await project.close(ignore_notification=True) + response = await client.get(app.url_path_for("get_nodes", project_id=project.id)) + assert response.status_code == status.HTTP_200_OK + assert response.json()[0]["name"] == "test" + + + async def test_get_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute + ) -> None: + + response = MagicMock() + response.json = {"console": 2048} + compute.post = AsyncioMagicMock(return_value=response) + + response = await client.post(app.url_path_for("create_node", project_id=project.id), json={ + "name": "test", + "node_type": "vpcs", + "compute_id": "example.com", + "properties": { + "startup_script": "echo test" + } + }) + + response = await client.get(app.url_path_for("get_node", project_id=project.id, node_id=response.json()["node_id"])) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + + + async def test_update_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = MagicMock() + response.json = {"console": 2048} + compute.put = AsyncioMagicMock(return_value=response) + + response = await client.put(app.url_path_for("update_node", project_id=project.id, node_id=node.id), json={ + "name": "test", + "node_type": "vpcs", + "compute_id": "example.com", + "properties": { + "startup_script": "echo test" + } + }) + + assert response.status_code == 200 + assert response.json()["name"] == "test" + assert "name" not in response.json()["properties"] + + + async def test_start_all_nodes( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("start_all_nodes", project_id=project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_stop_all_nodes( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("stop_all_nodes", project_id=project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_suspend_all_nodes( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("suspend_all_nodes", project_id=project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_reload_all_nodes( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("reload_all_nodes", project_id=project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_reset_console_all_nodes( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("reset_console_all_nodes", project_id=project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_start_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("start_node", project_id=project.id, node_id=node.id), json={}) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_stop_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("stop_node", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + async def test_suspend_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("suspend_node", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_reload_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ): + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("reload_node", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_isolate_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ): + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("isolate_node", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_unisolate_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.post(app.url_path_for("unisolate_node", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_duplicate_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = MagicMock() + response.json({"console": 2035}) + compute.post = AsyncioMagicMock(return_value=response) + + response = await client.post(app.url_path_for("duplicate_node", project_id=project.id, node_id=node.id), + json={"x": 10, "y": 5, "z": 0}) + assert response.status_code == status.HTTP_201_CREATED + + + async def test_delete_node( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + compute.post = AsyncioMagicMock() + response = await client.delete(app.url_path_for("delete_node", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_dynamips_idle_pc( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = MagicMock() + response.json = {"idlepc": "0x60606f54"} + compute.get = AsyncioMagicMock(return_value=response) + + node._node_type = "dynamips" # force Dynamips node type + response = await client.get(app.url_path_for("auto_idlepc", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_200_OK + assert response.json()["idlepc"] == "0x60606f54" + + + async def test_dynamips_idle_pc_wrong_node_type( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = await client.get(app.url_path_for("auto_idlepc", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_400_BAD_REQUEST + + + async def test_dynamips_idlepc_proposals( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = MagicMock() + response.json = ["0x60606f54", "0x33805a22"] + compute.get = AsyncioMagicMock(return_value=response) + + node._node_type = "dynamips" # force Dynamips node type + response = await client.get(app.url_path_for("idlepc_proposals", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_200_OK + assert response.json() == ["0x60606f54", "0x33805a22"] + + + async def test_dynamips_idlepc_proposals_wrong_node_type( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = await client.get(app.url_path_for("idlepc_proposals", project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_400_BAD_REQUEST + + + async def test_qemu_disk_image_create( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = MagicMock() + compute.post = AsyncioMagicMock(return_value=response) + + node._node_type = "qemu" # force Qemu node type + response = await client.post( + app.url_path_for("create_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"), + json={"format": "qcow2", "size": 30} + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_qemu_disk_image_create_wrong_node_type( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = await client.post( + app.url_path_for("create_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"), + json={"format": "qcow2", "size": 30} + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST + + + async def test_qemu_disk_image_update( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = MagicMock() + compute.put = AsyncioMagicMock(return_value=response) + + node._node_type = "qemu" # force Qemu node type + response = await client.put( + app.url_path_for("update_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"), + json={"extend": 10} + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_qemu_disk_image_update_wrong_node_type( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = await client.put( + app.url_path_for("update_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2"), + json={"extend": 10} + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST + + + async def test_qemu_disk_image_delete( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = MagicMock() + compute.delete = AsyncioMagicMock(return_value=response) + + node._node_type = "qemu" # force Qemu node type + response = await client.delete( + app.url_path_for("delete_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2") + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + + + async def test_qemu_disk_image_delete_wrong_node_type( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = await client.delete( + app.url_path_for("delete_disk_image", project_id=project.id, node_id=node.id, disk_name="hda_disk.qcow2") + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST + + + async def test_get_file( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + response = MagicMock() + response.body = b"world" + response.status = status.HTTP_200_OK + compute.http_query = AsyncioMagicMock(return_value=response) + + response = await client.get(app.url_path_for("get_file", project_id=project.id, node_id=node.id, file_path="hello")) + assert response.status_code == status.HTTP_200_OK + assert response.content == b'world' + + compute.http_query.assert_called_with( + "GET", + "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format( + project_id=project.id, + node_id=node.id), + timeout=None, + raw=True) + + response = await client.get(app.url_path_for( + "get_file", project_id=project.id, - node_id=node.id), - timeout=None, - raw=True) - - response = await client.get(app.url_path_for( - "get_file", - project_id=project.id, - node_id=node.id, - file_path="../hello")) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -async def test_post_file(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None: - - compute.http_query = AsyncioMagicMock() - response = await client.post(app.url_path_for( - "post_file", - project_id=project.id, - node_id=node.id, - file_path="hello"), content=b"hello") - assert response.status_code == status.HTTP_201_CREATED - - compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True) - - response = await client.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id)) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -# @pytest.mark.asyncio -# async def test_get_and_post_with_nested_paths_normalization(controller_api, project, node, compute): -# -# response = MagicMock() -# response.body = b"world" -# compute.http_query = AsyncioMagicMock(return_value=response) -# response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id)) -# assert response.status_code == 200 -# assert response.content == b'world' -# -# compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), timeout=None, raw=True) -# -# compute.http_query = AsyncioMagicMock() -# response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True) -# assert response.status_code == 201 -# -# compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True) + node_id=node.id, + file_path="../hello")) + assert response.status_code == status.HTTP_404_NOT_FOUND + + + async def test_post_file( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + compute: Compute, + node: Node + ) -> None: + + compute.http_query = AsyncioMagicMock() + response = await client.post(app.url_path_for( + "post_file", + project_id=project.id, + node_id=node.id, + file_path="hello"), content=b"hello") + assert response.status_code == status.HTTP_201_CREATED + + compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True) + + response = await client.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id)) + assert response.status_code == status.HTTP_404_NOT_FOUND + + + # @pytest.mark.asyncio + # async def test_get_and_post_with_nested_paths_normalization(controller_api, project, node, compute): + # + # response = MagicMock() + # response.body = b"world" + # compute.http_query = AsyncioMagicMock(return_value=response) + # response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id)) + # assert response.status_code == 200 + # assert response.content == b'world' + # + # compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), timeout=None, raw=True) + # + # compute.http_query = AsyncioMagicMock() + # response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello\\nested".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True) + # assert response.status_code == 201 + # + # compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello/nested".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True) diff --git a/tests/api/routes/controller/test_projects.py b/tests/api/routes/controller/test_projects.py index e1eac28b..cd3439c0 100644 --- a/tests/api/routes/controller/test_projects.py +++ b/tests/api/routes/controller/test_projects.py @@ -34,491 +34,544 @@ from gns3server.controller.compute import Compute pytestmark = pytest.mark.asyncio -@pytest_asyncio.fixture -async def project(app: FastAPI, client: AsyncClient, controller: Controller) -> Project: +class TestControllerProjectRoutes: - project_id = str(uuid.uuid4()) - params = {"name": "test", "project_id": project_id} - await client.post(app.url_path_for("create_project"), json=params) - return controller.get_project(project_id) - - -async def test_create_project_with_path(app: FastAPI, client: AsyncClient, controller: Controller, config) -> None: - - params = {"name": "test", "path": str(config.settings.Server.projects_path), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"} - response = await client.post(app.url_path_for("create_project"), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "test" - assert response.json()["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f" - assert response.json()["status"] == "opened" - - -async def test_create_project_without_dir(app: FastAPI, client: AsyncClient, controller: Controller) -> None: - - params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"} - response = await client.post(app.url_path_for("create_project"), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f" - assert response.json()["name"] == "test" - - -async def test_create_project_with_uuid(app: FastAPI, client: AsyncClient, controller: Controller) -> None: - - params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f"} - response = await client.post(app.url_path_for("create_project"), json=params) - assert response.status_code == 201 - assert response.json()["project_id"] == "30010203-0405-0607-0809-0a0b0c0d0e0f" - assert response.json()["name"] == "test" - - -async def test_create_project_with_variables(app: FastAPI, client: AsyncClient, controller: Controller) -> None: - - variables = [ - {"name": "TEST1"}, - {"name": "TEST2", "value": "value1"} - ] - params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables} - response = await client.post(app.url_path_for("create_project"), json=params) - assert response.status_code == 201 - assert response.json()["variables"] == [ - {"name": "TEST1"}, - {"name": "TEST2", "value": "value1"} - ] - - -async def test_create_project_with_supplier(app: FastAPI, client: AsyncClient, controller: Controller) -> None: - - supplier = { - 'logo': 'logo.png', - 'url': 'http://example.com/' - } - params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "supplier": supplier} - response = await client.post(app.url_path_for("create_project"), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["supplier"] == supplier - - -async def test_update_project(app: FastAPI, client: AsyncClient, controller: Controller) -> None: - - params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"} - response = await client.post(app.url_path_for("create_project"), json=params) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f" - assert response.json()["name"] == "test" - - params = {"name": "test2"} - response = await client.put(app.url_path_for("update_project", project_id="10010203-0405-0607-0809-0a0b0c0d0e0f"), - json=params) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test2" - - -async def test_update_project_with_variables(app: FastAPI, client: AsyncClient, controller: Controller) -> None: - - variables = [ - {"name": "TEST1"}, - {"name": "TEST2", "value": "value1"} - ] - params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables} - response = await client.post(app.url_path_for("create_project"), json=params) - assert response.status_code == status.HTTP_201_CREATED - - params = {"name": "test2"} - response = await client.put(app.url_path_for("update_project", project_id="10010203-0405-0607-0809-0a0b0c0d0e0f"), - json=params) - assert response.status_code == status.HTTP_200_OK - assert response.json()["variables"] == variables - - -async def test_list_projects(app: FastAPI, client: AsyncClient, controller: Controller) -> None: - - params = {"name": "test", "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"} - await client.post(app.url_path_for("create_project"), json=params) - response = await client.get(app.url_path_for("get_projects")) - assert response.status_code == status.HTTP_200_OK - projects = response.json() - assert projects[0]["name"] == "test" - - -async def test_get_project(app: FastAPI, client: AsyncClient, project: Project) -> None: - - response = await client.get(app.url_path_for("get_project", project_id=project.id)) - assert response.status_code == status.HTTP_200_OK - assert response.json()["name"] == "test" - - -async def test_delete_project(app: FastAPI, client: AsyncClient, project: Project, controller: Controller) -> None: - - with asyncio_patch("gns3server.controller.project.Project.delete", return_value=True) as mock: - response = await client.delete(app.url_path_for("delete_project", project_id=project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert mock.called - assert project not in controller.projects - - -async def test_delete_project_invalid_uuid(app: FastAPI, client: AsyncClient) -> None: - - response = await client.delete(app.url_path_for("delete_project", project_id=str(uuid.uuid4()))) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -async def test_close_project(app: FastAPI, client: AsyncClient, project: Project) -> None: - - with asyncio_patch("gns3server.controller.project.Project.close", return_value=True) as mock: - response = await client.post(app.url_path_for("close_project", project_id=project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert mock.called - - -async def test_open_project(app: FastAPI, client: AsyncClient, project: Project) -> None: - - with asyncio_patch("gns3server.controller.project.Project.open", return_value=True) as mock: - response = await client.post(app.url_path_for("open_project", project_id=project.id)) + @pytest_asyncio.fixture + async def project(self, app: FastAPI, client: AsyncClient, controller: Controller) -> Project: + + project_id = str(uuid.uuid4()) + params = {"name": "test", "project_id": project_id} + await client.post(app.url_path_for("create_project"), json=params) + return controller.get_project(project_id) + + + async def test_create_project_with_path( + self, + app: FastAPI, + client: AsyncClient, + controller: Controller, + config + ) -> None: + + params = {"name": "test", "path": str(config.settings.Server.projects_path), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"} + response = await client.post(app.url_path_for("create_project"), json=params) assert response.status_code == status.HTTP_201_CREATED - assert mock.called - - -async def test_load_project(app: FastAPI, client: AsyncClient, project: Project, config) -> None: - - with asyncio_patch("gns3server.controller.Controller.load_project", return_value=project) as mock: - response = await client.post(app.url_path_for("load_project"), json={"path": "/tmp/test.gns3"}) + assert response.json()["name"] == "test" + assert response.json()["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f" + assert response.json()["status"] == "opened" + + + async def test_create_project_without_dir( + self, + app: FastAPI, + client: AsyncClient, + controller: Controller + ) -> None: + + params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"} + response = await client.post(app.url_path_for("create_project"), json=params) assert response.status_code == status.HTTP_201_CREATED - mock.assert_called_with("/tmp/test.gns3") - assert response.json()["project_id"] == project.id + assert response.json()["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f" + assert response.json()["name"] == "test" + + + async def test_create_project_with_uuid( + self, + app: FastAPI, + client: AsyncClient, + controller: Controller + ) -> None: + + params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f"} + response = await client.post(app.url_path_for("create_project"), json=params) + assert response.status_code == 201 + assert response.json()["project_id"] == "30010203-0405-0607-0809-0a0b0c0d0e0f" + assert response.json()["name"] == "test" + + + async def test_create_project_with_variables( + self, + app: FastAPI, + client: AsyncClient, + controller: Controller + ) -> None: + + variables = [ + {"name": "TEST1"}, + {"name": "TEST2", "value": "value1"} + ] + params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables} + response = await client.post(app.url_path_for("create_project"), json=params) + assert response.status_code == 201 + assert response.json()["variables"] == [ + {"name": "TEST1"}, + {"name": "TEST2", "value": "value1"} + ] + - -# @pytest.mark.asyncio -# async def test_notification(controller_api, http_client, project, controller): -# -# async with http_client.get(controller_api.get_url("/projects/{project_id}/notifications".format(project_id=project.id))) as response: -# response.body = await response.content.read(200) -# controller.notification.project_emit("node.created", {"a": "b"}) -# response.body += await response.content.readany() -# assert response.status_code == 200 -# assert b'"action": "ping"' in response.body -# assert b'"cpu_usage_percent"' in response.body -# assert b'{"action": "node.created", "event": {"a": "b"}}\n' in response.body -# assert project.status_code == "opened" -# -# -# @pytest.mark.asyncio -# async def test_notification_invalid_id(controller_api): -# -# response = await controller_api.get("/projects/{project_id}/notifications".format(project_id=uuid.uuid4())) -# assert response.status_code == 404 - - -# @pytest.mark.asyncio -# async def test_notification_ws(controller_api, http_client, controller, project): -# -# ws = await http_client.ws_connect(controller_api.get_url("/projects/{project_id}/notifications/ws".format(project_id=project.id))) -# answer = await ws.receive() -# answer = json.loads(answer.data) -# assert answer["action"] == "ping" -# -# controller.notification.project_emit("test", {}) -# answer = await ws.receive() -# answer = json.loads(answer.data) -# assert answer["action"] == "test" -# -# if not ws.closed: -# await ws.close() -# -# assert project.status_code == "opened" - - -async def test_export_with_images(app: FastAPI, client: AsyncClient, tmpdir, project: Project) -> None: - - project.dump = MagicMock() - os.makedirs(project.path, exist_ok=True) - with open(os.path.join(project.path, 'a'), 'w+') as f: - f.write('hello') - - os.makedirs(str(tmpdir / "IOS")) - with open(str(tmpdir / "IOS" / "test.image"), "w+") as f: - f.write("AAA") - - topology = { - "topology": { - "nodes": [ - { - "properties": { - "image": "test.image" - }, - "node_type": "dynamips" - } - ] + async def test_create_project_with_supplier( + self, + app: FastAPI, + client: AsyncClient, + controller: Controller + ) -> None: + + supplier = { + 'logo': 'logo.png', + 'url': 'http://example.com/' } - } - with open(os.path.join(project.path, "test.gns3"), 'w+') as f: - json.dump(topology, f) - - with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS")): - response = await client.get(app.url_path_for("export_project", project_id=project.id), - params={"include_images": "yes"}) - assert response.status_code == status.HTTP_200_OK - assert response.headers['CONTENT-TYPE'] == 'application/gns3project' - assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) - - with open(str(tmpdir / 'project.zip'), 'wb+') as f: - f.write(response.content) - - with zipfile_zstd.ZipFile(str(tmpdir / 'project.zip')) as myzip: - with myzip.open("a") as myfile: - content = myfile.read() - assert content == b"hello" - myzip.getinfo("images/IOS/test.image") - - -async def test_export_without_images(app: FastAPI, client: AsyncClient, tmpdir, project: Project) -> None: - - project.dump = MagicMock() - os.makedirs(project.path, exist_ok=True) - with open(os.path.join(project.path, 'a'), 'w+') as f: - f.write('hello') - - os.makedirs(str(tmpdir / "IOS")) - with open(str(tmpdir / "IOS" / "test.image"), "w+") as f: - f.write("AAA") - - topology = { - "topology": { - "nodes": [ - { - "properties": { - "image": "test.image" - }, - "node_type": "dynamips" - } - ] + params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "supplier": supplier} + response = await client.post(app.url_path_for("create_project"), json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["supplier"] == supplier + + + async def test_update_project(self, app: FastAPI, client: AsyncClient, controller: Controller) -> None: + + params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"} + response = await client.post(app.url_path_for("create_project"), json=params) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f" + assert response.json()["name"] == "test" + + params = {"name": "test2"} + response = await client.put(app.url_path_for("update_project", project_id="10010203-0405-0607-0809-0a0b0c0d0e0f"), + json=params) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test2" + + + async def test_update_project_with_variables( + self, + app: FastAPI, + client: AsyncClient, + controller: Controller + ) -> None: + + variables = [ + {"name": "TEST1"}, + {"name": "TEST2", "value": "value1"} + ] + params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables} + response = await client.post(app.url_path_for("create_project"), json=params) + assert response.status_code == status.HTTP_201_CREATED + + params = {"name": "test2"} + response = await client.put(app.url_path_for("update_project", project_id="10010203-0405-0607-0809-0a0b0c0d0e0f"), + json=params) + assert response.status_code == status.HTTP_200_OK + assert response.json()["variables"] == variables + + + async def test_list_projects(self, app: FastAPI, client: AsyncClient, controller: Controller) -> None: + + params = {"name": "test", "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"} + await client.post(app.url_path_for("create_project"), json=params) + response = await client.get(app.url_path_for("get_projects")) + assert response.status_code == status.HTTP_200_OK + projects = response.json() + assert projects[0]["name"] == "test" + + + async def test_get_project(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + response = await client.get(app.url_path_for("get_project", project_id=project.id)) + assert response.status_code == status.HTTP_200_OK + assert response.json()["name"] == "test" + + + async def test_delete_project( + self, + app: FastAPI, + client: AsyncClient, + project: Project, + controller: Controller + ) -> None: + + with asyncio_patch("gns3server.controller.project.Project.delete", return_value=True) as mock: + response = await client.delete(app.url_path_for("delete_project", project_id=project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called + assert project not in controller.projects + + + async def test_delete_project_invalid_uuid(self, app: FastAPI, client: AsyncClient) -> None: + + response = await client.delete(app.url_path_for("delete_project", project_id=str(uuid.uuid4()))) + assert response.status_code == status.HTTP_404_NOT_FOUND + + + async def test_close_project(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + with asyncio_patch("gns3server.controller.project.Project.close", return_value=True) as mock: + response = await client.post(app.url_path_for("close_project", project_id=project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert mock.called + + + async def test_open_project(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + with asyncio_patch("gns3server.controller.project.Project.open", return_value=True) as mock: + response = await client.post(app.url_path_for("open_project", project_id=project.id)) + assert response.status_code == status.HTTP_201_CREATED + assert mock.called + + + async def test_load_project(self, app: FastAPI, client: AsyncClient, project: Project, config) -> None: + + with asyncio_patch("gns3server.controller.Controller.load_project", return_value=project) as mock: + response = await client.post(app.url_path_for("load_project"), json={"path": "/tmp/test.gns3"}) + assert response.status_code == status.HTTP_201_CREATED + mock.assert_called_with("/tmp/test.gns3") + assert response.json()["project_id"] == project.id + + + # @pytest.mark.asyncio + # async def test_notification(controller_api, http_client, project, controller): + # + # async with http_client.get(controller_api.get_url("/projects/{project_id}/notifications".format(project_id=project.id))) as response: + # response.body = await response.content.read(200) + # controller.notification.project_emit("node.created", {"a": "b"}) + # response.body += await response.content.readany() + # assert response.status_code == 200 + # assert b'"action": "ping"' in response.body + # assert b'"cpu_usage_percent"' in response.body + # assert b'{"action": "node.created", "event": {"a": "b"}}\n' in response.body + # assert project.status_code == "opened" + # + # + # @pytest.mark.asyncio + # async def test_notification_invalid_id(controller_api): + # + # response = await controller_api.get("/projects/{project_id}/notifications".format(project_id=uuid.uuid4())) + # assert response.status_code == 404 + + + # @pytest.mark.asyncio + # async def test_notification_ws(controller_api, http_client, controller, project): + # + # ws = await http_client.ws_connect(controller_api.get_url("/projects/{project_id}/notifications/ws".format(project_id=project.id))) + # answer = await ws.receive() + # answer = json.loads(answer.data) + # assert answer["action"] == "ping" + # + # controller.notification.project_emit("test", {}) + # answer = await ws.receive() + # answer = json.loads(answer.data) + # assert answer["action"] == "test" + # + # if not ws.closed: + # await ws.close() + # + # assert project.status_code == "opened" + + + async def test_export_with_images( + self, + app: FastAPI, + client: AsyncClient, + tmpdir, + project: Project + ) -> None: + + project.dump = MagicMock() + os.makedirs(project.path, exist_ok=True) + with open(os.path.join(project.path, 'a'), 'w+') as f: + f.write('hello') + + os.makedirs(str(tmpdir / "IOS")) + with open(str(tmpdir / "IOS" / "test.image"), "w+") as f: + f.write("AAA") + + topology = { + "topology": { + "nodes": [ + { + "properties": { + "image": "test.image" + }, + "node_type": "dynamips" + } + ] + } } - } - with open(os.path.join(project.path, "test.gns3"), 'w+') as f: - json.dump(topology, f) - - with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): - response = await client.get(app.url_path_for("export_project", project_id=project.id), - params={"include_images": "0"}) - assert response.status_code == status.HTTP_200_OK - assert response.headers['CONTENT-TYPE'] == 'application/gns3project' - assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) - - with open(str(tmpdir / 'project.zip'), 'wb+') as f: - f.write(response.content) - - with zipfile_zstd.ZipFile(str(tmpdir / 'project.zip')) as myzip: - with myzip.open("a") as myfile: - content = myfile.read() - assert content == b"hello" - # Image should not exported - with pytest.raises(KeyError): - myzip.getinfo("images/IOS/test.image") - - -@pytest.mark.parametrize( - "compression, compression_level, status_code", - ( - ("none", None, status.HTTP_200_OK), - ("none", 4, status.HTTP_400_BAD_REQUEST), - ("zip", None, status.HTTP_200_OK), - ("zip", 1, status.HTTP_200_OK), - ("zip", 12, status.HTTP_400_BAD_REQUEST), - ("bzip2", None, status.HTTP_200_OK), - ("bzip2", 1, status.HTTP_200_OK), - ("bzip2", 13, status.HTTP_400_BAD_REQUEST), - ("lzma", None, status.HTTP_200_OK), - ("lzma", 1, status.HTTP_400_BAD_REQUEST), - ("zstd", None, status.HTTP_200_OK), - ("zstd", 12, status.HTTP_200_OK), - ("zstd", 23, status.HTTP_400_BAD_REQUEST), - ) -) -async def test_export_compression( - app: FastAPI, - client: AsyncClient, - tmpdir, - project: Project, - compression: str, - compression_level: int, - status_code: int -) -> None: - - project.dump = MagicMock() - os.makedirs(project.path, exist_ok=True) - - topology = { - "topology": { - "nodes": [ - { - "node_type": "qemu" - } - ] - } - } - with open(os.path.join(project.path, "test.gns3"), 'w+') as f: - json.dump(topology, f) - - params = {"compression": compression} - if compression_level: - params["compression_level"] = compression_level - response = await client.get(app.url_path_for("export_project", project_id=project.id), params=params) - assert response.status_code == status_code - - if response.status_code == status.HTTP_200_OK: + with open(os.path.join(project.path, "test.gns3"), 'w+') as f: + json.dump(topology, f) + + with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS")): + response = await client.get(app.url_path_for("export_project", project_id=project.id), + params={"include_images": "yes"}) + assert response.status_code == status.HTTP_200_OK assert response.headers['CONTENT-TYPE'] == 'application/gns3project' assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) - + with open(str(tmpdir / 'project.zip'), 'wb+') as f: f.write(response.content) - + with zipfile_zstd.ZipFile(str(tmpdir / 'project.zip')) as myzip: - with myzip.open("project.gns3") as myfile: - myfile.read() - - -async def test_get_file(app: FastAPI, client: AsyncClient, project: Project) -> None: - - os.makedirs(project.path, exist_ok=True) - with open(os.path.join(project.path, 'hello'), 'w+') as f: - f.write('world') - - response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="hello")) - assert response.status_code == status.HTTP_200_OK - assert response.content == b"world" - - response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="false")) - assert response.status_code == status.HTTP_404_NOT_FOUND - - response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="../hello")) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -async def test_get_file_forbidden_location(app: FastAPI, client: AsyncClient, project: Project) -> None: - - file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" - response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path=file_path)) - assert response.status_code == status.HTTP_403_FORBIDDEN - - -async def test_write_file(app: FastAPI, client: AsyncClient, project: Project) -> None: - - response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path="hello"), - content=b"world") - assert response.status_code == status.HTTP_204_NO_CONTENT - - with open(os.path.join(project.path, "hello")) as f: - assert f.read() == "world" - - response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path="../hello")) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -async def test_write_file_forbidden_location(app: FastAPI, client: AsyncClient, project: Project) -> None: - - file_path = "%2e%2e/hello" - response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path=file_path), - content=b"world") - assert response.status_code == status.HTTP_403_FORBIDDEN - - -async def test_write_and_get_file_with_leading_slashes_in_filename( - app: FastAPI, - client: AsyncClient, - project: Project) -> None: - - response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path="//hello"), - content=b"world") - assert response.status_code == status.HTTP_403_FORBIDDEN - - response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="//hello")) - assert response.status_code == status.HTTP_403_FORBIDDEN - - -async def test_import(app: FastAPI, client: AsyncClient, tmpdir, controller: Controller) -> None: - - with zipfile_zstd.ZipFile(str(tmpdir / "test.zip"), 'w') as myzip: - myzip.writestr("project.gns3", b'{"project_id": "c6992992-ac72-47dc-833b-54aa334bcd05", "version": "2.0.0", "name": "test"}') - myzip.writestr("demo", b"hello") - - project_id = str(uuid.uuid4()) - with open(str(tmpdir / "test.zip"), "rb") as f: - response = await client.post(app.url_path_for("import_project", project_id=project_id), content=f.read()) - assert response.status_code == status.HTTP_201_CREATED - - project = controller.get_project(project_id) - with open(os.path.join(project.path, "demo")) as f: - content = f.read() - assert content == "hello" - - -async def test_import_with_project_name(app: FastAPI, client: AsyncClient, tmpdir, controller: Controller) -> None: - - with zipfile_zstd.ZipFile(str(tmpdir / "test.zip"), 'w') as myzip: - myzip.writestr("project.gns3", b'{"project_id": "c6992992-ac72-47dc-833b-54aa334bcd05", "version": "2.0.0", "name": "test"}') - myzip.writestr("demo", b"hello") - - project_id = str(uuid.uuid4()) - with open(str(tmpdir / "test.zip"), "rb") as f: - response = await client.post( - app.url_path_for("import_project", project_id=project_id), - content=f.read(), - params={"name": "my-imported-project-name"} - ) - assert response.status_code == status.HTTP_201_CREATED - project = controller.get_project(project_id) - assert project.name == "my-imported-project-name" - - -async def test_duplicate(app: FastAPI, client: AsyncClient, project: Project) -> None: - - response = await client.post(app.url_path_for("duplicate_project", project_id=project.id), json={"name": "hello"}) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == "hello" - - -async def test_lock_unlock(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: - - # add a drawing and node to the project - params = { - "svg": '', - "x": 10, - "y": 20, - "z": 0 - } - - response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) - assert response.status_code == status.HTTP_201_CREATED - - response = MagicMock() - response.json = {"console": 2048} - compute.post = AsyncioMagicMock(return_value=response) - - response = await client.post(app.url_path_for("create_node", project_id=project.id), json={ - "name": "test", - "node_type": "vpcs", - "compute_id": "example.com", - "properties": { - "startup_script": "echo test" + with myzip.open("a") as myfile: + content = myfile.read() + assert content == b"hello" + myzip.getinfo("images/IOS/test.image") + + + async def test_export_without_images(self, app: FastAPI, client: AsyncClient, tmpdir, project: Project) -> None: + + project.dump = MagicMock() + os.makedirs(project.path, exist_ok=True) + with open(os.path.join(project.path, 'a'), 'w+') as f: + f.write('hello') + + os.makedirs(str(tmpdir / "IOS")) + with open(str(tmpdir / "IOS" / "test.image"), "w+") as f: + f.write("AAA") + + topology = { + "topology": { + "nodes": [ + { + "properties": { + "image": "test.image" + }, + "node_type": "dynamips" + } + ] + } } - }) - assert response.status_code == status.HTTP_201_CREATED - - response = await client.post(app.url_path_for("lock_project", project_id=project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - for drawing in project.drawings.values(): - assert drawing.locked is True - for node in project.nodes.values(): - assert node.locked is True - - response = await client.get(app.url_path_for("locked_project", project_id=project.id)) - assert response.status_code == status.HTTP_200_OK - assert response.json() is True - - response = await client.post(app.url_path_for("unlock_project", project_id=project.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - - for drawing in project.drawings.values(): - assert drawing.locked is False - for node in project.nodes.values(): - assert node.locked is False + with open(os.path.join(project.path, "test.gns3"), 'w+') as f: + json.dump(topology, f) + + with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): + response = await client.get(app.url_path_for("export_project", project_id=project.id), + params={"include_images": "0"}) + assert response.status_code == status.HTTP_200_OK + assert response.headers['CONTENT-TYPE'] == 'application/gns3project' + assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) + + with open(str(tmpdir / 'project.zip'), 'wb+') as f: + f.write(response.content) + + with zipfile_zstd.ZipFile(str(tmpdir / 'project.zip')) as myzip: + with myzip.open("a") as myfile: + content = myfile.read() + assert content == b"hello" + # Image should not exported + with pytest.raises(KeyError): + myzip.getinfo("images/IOS/test.image") + + + @pytest.mark.parametrize( + "compression, compression_level, status_code", + ( + ("none", None, status.HTTP_200_OK), + ("none", 4, status.HTTP_400_BAD_REQUEST), + ("zip", None, status.HTTP_200_OK), + ("zip", 1, status.HTTP_200_OK), + ("zip", 12, status.HTTP_400_BAD_REQUEST), + ("bzip2", None, status.HTTP_200_OK), + ("bzip2", 1, status.HTTP_200_OK), + ("bzip2", 13, status.HTTP_400_BAD_REQUEST), + ("lzma", None, status.HTTP_200_OK), + ("lzma", 1, status.HTTP_400_BAD_REQUEST), + ("zstd", None, status.HTTP_200_OK), + ("zstd", 12, status.HTTP_200_OK), + ("zstd", 23, status.HTTP_400_BAD_REQUEST), + ) + ) + async def test_export_compression( + self, + app: FastAPI, + client: AsyncClient, + tmpdir, + project: Project, + compression: str, + compression_level: int, + status_code: int + ) -> None: + + project.dump = MagicMock() + os.makedirs(project.path, exist_ok=True) + + topology = { + "topology": { + "nodes": [ + { + "node_type": "qemu" + } + ] + } + } + with open(os.path.join(project.path, "test.gns3"), 'w+') as f: + json.dump(topology, f) + + params = {"compression": compression} + if compression_level: + params["compression_level"] = compression_level + response = await client.get(app.url_path_for("export_project", project_id=project.id), params=params) + assert response.status_code == status_code + + if response.status_code == status.HTTP_200_OK: + assert response.headers['CONTENT-TYPE'] == 'application/gns3project' + assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) + + with open(str(tmpdir / 'project.zip'), 'wb+') as f: + f.write(response.content) + + with zipfile_zstd.ZipFile(str(tmpdir / 'project.zip')) as myzip: + with myzip.open("project.gns3") as myfile: + myfile.read() + + + async def test_get_file(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + os.makedirs(project.path, exist_ok=True) + with open(os.path.join(project.path, 'hello'), 'w+') as f: + f.write('world') + + response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="hello")) + assert response.status_code == status.HTTP_200_OK + assert response.content == b"world" + + response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="false")) + assert response.status_code == status.HTTP_404_NOT_FOUND + + response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="../hello")) + assert response.status_code == status.HTTP_404_NOT_FOUND + + + async def test_get_file_forbidden_location(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + file_path = "foo/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd" + response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path=file_path)) + assert response.status_code == status.HTTP_403_FORBIDDEN + + + async def test_write_file(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path="hello"), + content=b"world") + assert response.status_code == status.HTTP_204_NO_CONTENT + + with open(os.path.join(project.path, "hello")) as f: + assert f.read() == "world" + + response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path="../hello")) + assert response.status_code == status.HTTP_404_NOT_FOUND + + + async def test_write_file_forbidden_location(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + file_path = "%2e%2e/hello" + response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path=file_path), + content=b"world") + assert response.status_code == status.HTTP_403_FORBIDDEN + + + async def test_write_and_get_file_with_leading_slashes_in_filename( + self, + app: FastAPI, + client: AsyncClient, + project: Project) -> None: + + response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path="//hello"), + content=b"world") + assert response.status_code == status.HTTP_403_FORBIDDEN + + response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="//hello")) + assert response.status_code == status.HTTP_403_FORBIDDEN + + + async def test_import(self, app: FastAPI, client: AsyncClient, tmpdir, controller: Controller) -> None: + + with zipfile_zstd.ZipFile(str(tmpdir / "test.zip"), 'w') as myzip: + myzip.writestr("project.gns3", b'{"project_id": "c6992992-ac72-47dc-833b-54aa334bcd05", "version": "2.0.0", "name": "test"}') + myzip.writestr("demo", b"hello") + + project_id = str(uuid.uuid4()) + with open(str(tmpdir / "test.zip"), "rb") as f: + response = await client.post(app.url_path_for("import_project", project_id=project_id), content=f.read()) + assert response.status_code == status.HTTP_201_CREATED + + project = controller.get_project(project_id) + with open(os.path.join(project.path, "demo")) as f: + content = f.read() + assert content == "hello" + + + async def test_import_with_project_name( + self, + app: FastAPI, + client: AsyncClient, + tmpdir, + controller: Controller + ) -> None: + + with zipfile_zstd.ZipFile(str(tmpdir / "test.zip"), 'w') as myzip: + myzip.writestr("project.gns3", b'{"project_id": "c6992992-ac72-47dc-833b-54aa334bcd05", "version": "2.0.0", "name": "test"}') + myzip.writestr("demo", b"hello") + + project_id = str(uuid.uuid4()) + with open(str(tmpdir / "test.zip"), "rb") as f: + response = await client.post( + app.url_path_for("import_project", project_id=project_id), + content=f.read(), + params={"name": "my-imported-project-name"} + ) + assert response.status_code == status.HTTP_201_CREATED + project = controller.get_project(project_id) + assert project.name == "my-imported-project-name" + + + async def test_duplicate(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + response = await client.post(app.url_path_for("duplicate_project", project_id=project.id), json={"name": "hello"}) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == "hello" + + + async def test_lock_unlock(self, app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None: + + # add a drawing and node to the project + params = { + "svg": '', + "x": 10, + "y": 20, + "z": 0 + } + + response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params) + assert response.status_code == status.HTTP_201_CREATED + + response = MagicMock() + response.json = {"console": 2048} + compute.post = AsyncioMagicMock(return_value=response) + + response = await client.post(app.url_path_for("create_node", project_id=project.id), json={ + "name": "test", + "node_type": "vpcs", + "compute_id": "example.com", + "properties": { + "startup_script": "echo test" + } + }) + assert response.status_code == status.HTTP_201_CREATED + + response = await client.post(app.url_path_for("lock_project", project_id=project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + for drawing in project.drawings.values(): + assert drawing.locked is True + for node in project.nodes.values(): + assert node.locked is True + + response = await client.get(app.url_path_for("locked_project", project_id=project.id)) + assert response.status_code == status.HTTP_200_OK + assert response.json() is True + + response = await client.post(app.url_path_for("unlock_project", project_id=project.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + + for drawing in project.drawings.values(): + assert drawing.locked is False + for node in project.nodes.values(): + assert node.locked is False diff --git a/tests/api/routes/controller/test_snapshots.py b/tests/api/routes/controller/test_snapshots.py index 3058cdad..531c3fe2 100644 --- a/tests/api/routes/controller/test_snapshots.py +++ b/tests/api/routes/controller/test_snapshots.py @@ -30,47 +30,49 @@ from gns3server.controller.snapshot import Snapshot pytestmark = pytest.mark.asyncio -@pytest_asyncio.fixture -async def project(app: FastAPI, client: AsyncClient, controller: Controller) -> Project: +class TestSnapshotRoutes: - u = str(uuid.uuid4()) - params = {"name": "test", "project_id": u} - await client.post(app.url_path_for("create_project"), json=params) - project = controller.get_project(u) - return project - - -@pytest_asyncio.fixture -async def snapshot(project: Project): - - snapshot = await project.snapshot("test") - return snapshot - - -async def test_list_snapshots(app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None: - - assert snapshot.name == "test" - response = await client.get(app.url_path_for("get_snapshots", project_id=project.id)) - assert response.status_code == status.HTTP_200_OK - assert len(response.json()) == 1 - - -async def test_delete_snapshot(app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None: - - response = await client.delete(app.url_path_for("delete_snapshot", project_id=project.id, snapshot_id=snapshot.id)) - assert response.status_code == status.HTTP_204_NO_CONTENT - assert not os.path.exists(snapshot.path) - - -async def test_restore_snapshot(app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None: - - response = await client.post(app.url_path_for("restore_snapshot", project_id=project.id, snapshot_id=snapshot.id)) - assert response.status_code == status.HTTP_201_CREATED - assert response.json()["name"] == project.name - - -async def test_create_snapshot(app: FastAPI, client: AsyncClient, project: Project) -> None: - - response = await client.post(app.url_path_for("create_snapshot", project_id=project.id), json={"name": "snap1"}) - assert response.status_code == status.HTTP_201_CREATED - assert len(os.listdir(os.path.join(project.path, "snapshots"))) == 1 + @pytest_asyncio.fixture + async def project(self, app: FastAPI, client: AsyncClient, controller: Controller) -> Project: + + u = str(uuid.uuid4()) + params = {"name": "test", "project_id": u} + await client.post(app.url_path_for("create_project"), json=params) + controller_project = controller.get_project(u) + return controller_project + + + @pytest_asyncio.fixture + async def snapshot(self, project: Project): + + controller_snapshot = await project.snapshot("test") + return controller_snapshot + + + async def test_list_snapshots(self, app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None: + + assert snapshot.name == "test" + response = await client.get(app.url_path_for("get_snapshots", project_id=project.id)) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()) == 1 + + + async def test_delete_snapshot(self, app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None: + + response = await client.delete(app.url_path_for("delete_snapshot", project_id=project.id, snapshot_id=snapshot.id)) + assert response.status_code == status.HTTP_204_NO_CONTENT + assert not os.path.exists(snapshot.path) + + + async def test_restore_snapshot(self, app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None: + + response = await client.post(app.url_path_for("restore_snapshot", project_id=project.id, snapshot_id=snapshot.id)) + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == project.name + + + async def test_create_snapshot(self, app: FastAPI, client: AsyncClient, project: Project) -> None: + + response = await client.post(app.url_path_for("create_snapshot", project_id=project.id), json={"name": "snap1"}) + assert response.status_code == status.HTTP_201_CREATED + assert len(os.listdir(os.path.join(project.path, "snapshots"))) == 1 diff --git a/tests/api/routes/controller/test_symbols.py b/tests/api/routes/controller/test_symbols.py index c6941bdb..24e60720 100644 --- a/tests/api/routes/controller/test_symbols.py +++ b/tests/api/routes/controller/test_symbols.py @@ -28,41 +28,43 @@ from gns3server.controller import Controller pytestmark = pytest.mark.asyncio -async def test_symbols(app: FastAPI, client: AsyncClient) -> None: +class TestSymbolRoutes: - response = await client.get(app.url_path_for("get_symbols")) - - assert response.status_code == status.HTTP_200_OK - assert { - 'symbol_id': ':/symbols/classic/firewall.svg', - 'filename': 'firewall.svg', - 'builtin': True, - 'theme': 'Classic' - } in response.json() - - -async def test_get(app: FastAPI, client: AsyncClient, controller: Controller) -> None: - - controller.symbols.theme = "Classic" - url = app.url_path_for("get_symbol", symbol_id=urllib.parse.quote(':/symbols/classic/firewall.svg')) - response = await client.get(url) - assert response.status_code == status.HTTP_200_OK - assert response.headers['CONTENT-TYPE'] == 'image/svg+xml' - assert response.headers['CONTENT-LENGTH'] == '9381' - assert '' in response.text - - # Reply with the default symbol - response = await client.get(app.url_path_for("get_symbol", symbol_id="404.png")) - assert response.status_code == status.HTTP_200_OK - - -async def test_upload(app: FastAPI, client: AsyncClient, symbols_dir: str) -> None: - - response = await client.post(app.url_path_for("upload_symbol", symbol_id="test2"), content=b"TEST") - assert response.status_code == status.HTTP_204_NO_CONTENT - - with open(os.path.join(symbols_dir, "test2")) as f: - assert f.read() == "TEST" - - response = await client.get(app.url_path_for("get_symbol", symbol_id="test2")) - assert response.status_code == status.HTTP_200_OK + async def test_symbols(self, app: FastAPI, client: AsyncClient) -> None: + + response = await client.get(app.url_path_for("get_symbols")) + + assert response.status_code == status.HTTP_200_OK + assert { + 'symbol_id': ':/symbols/classic/firewall.svg', + 'filename': 'firewall.svg', + 'builtin': True, + 'theme': 'Classic' + } in response.json() + + + async def test_get(self, app: FastAPI, client: AsyncClient, controller: Controller) -> None: + + controller.symbols.theme = "Classic" + url = app.url_path_for("get_symbol", symbol_id=urllib.parse.quote(':/symbols/classic/firewall.svg')) + response = await client.get(url) + assert response.status_code == status.HTTP_200_OK + assert response.headers['CONTENT-TYPE'] == 'image/svg+xml' + assert response.headers['CONTENT-LENGTH'] == '9381' + assert '' in response.text + + # Reply with the default symbol + response = await client.get(app.url_path_for("get_symbol", symbol_id="404.png")) + assert response.status_code == status.HTTP_200_OK + + + async def test_upload(self, app: FastAPI, client: AsyncClient, symbols_dir: str) -> None: + + response = await client.post(app.url_path_for("upload_symbol", symbol_id="test2"), content=b"TEST") + assert response.status_code == status.HTTP_204_NO_CONTENT + + with open(os.path.join(symbols_dir, "test2")) as f: + assert f.read() == "TEST" + + response = await client.get(app.url_path_for("get_symbol", symbol_id="test2")) + assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/controller/test_users.py b/tests/api/routes/controller/test_users.py index 416fe45b..87ea7470 100644 --- a/tests/api/routes/controller/test_users.py +++ b/tests/api/routes/controller/test_users.py @@ -254,22 +254,6 @@ class TestUserLogin: assert "token_type" in response.json() assert response.json().get("token_type") == "bearer" - async def test_user_can_authenticate_using_json( - self, - app: FastAPI, - unauthorized_client: AsyncClient, - test_user: User, - config: Config - ) -> None: - - credentials = { - "username": test_user.username, - "password": "user1_password", - } - response = await unauthorized_client.post(app.url_path_for("authenticate"), json=credentials) - assert response.status_code == status.HTTP_200_OK - assert response.json().get("access_token") - @pytest.mark.parametrize( "username, password, status_code", ( @@ -299,7 +283,19 @@ class TestUserLogin: assert response.status_code == status_code assert "access_token" not in response.json() - async def test_user_can_use_token_as_url_param( + +class TestUnauthorizedUser: + + async def test_user_cannot_access_own_data_if_not_authenticated( + self, app: FastAPI, + unauthorized_client: AsyncClient, + test_user: User, + ) -> None: + + response = await unauthorized_client.get(app.url_path_for("get_logged_in_user")) + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + async def test_user_can_authenticate_using_json( self, app: FastAPI, unauthorized_client: AsyncClient, @@ -311,15 +307,14 @@ class TestUserLogin: "username": test_user.username, "password": "user1_password", } - response = await unauthorized_client.post(app.url_path_for("authenticate"), json=credentials) assert response.status_code == status.HTTP_200_OK - token = response.json().get("access_token") + assert response.json().get("access_token") + token = response.json().get("access_token") response = await unauthorized_client.get(app.url_path_for("statistics"), params={"token": token}) assert response.status_code == status.HTTP_200_OK - class TestUserMe: async def test_authenticated_user_can_retrieve_own_data( @@ -336,15 +331,6 @@ class TestUserMe: assert user.email == test_user.email assert user.user_id == test_user.user_id - async def test_user_cannot_access_own_data_if_not_authenticated( - self, app: FastAPI, - unauthorized_client: AsyncClient, - test_user: User, - ) -> None: - - response = await unauthorized_client.get(app.url_path_for("get_logged_in_user")) - assert response.status_code == status.HTTP_401_UNAUTHORIZED - async def test_authenticated_user_can_update_own_data( self, app: FastAPI, diff --git a/tests/api/routes/controller/test_version.py b/tests/api/routes/controller/test_version.py index 27ca7cb3..1e229ddd 100644 --- a/tests/api/routes/controller/test_version.py +++ b/tests/api/routes/controller/test_version.py @@ -25,38 +25,41 @@ from gns3server.version import __version__ pytestmark = pytest.mark.asyncio -async def test_version_output(app: FastAPI, client: AsyncClient) -> None: - - response = await client.get(app.url_path_for("get_version")) - assert response.status_code == status.HTTP_200_OK - assert response.json() == {'controller_host': '127.0.0.1', 'local': False, 'version': __version__} +class TestVersionRoutes: -async def test_version_input(app: FastAPI, client: AsyncClient) -> None: + async def test_version_output(self, app: FastAPI, client: AsyncClient) -> None: - params = {'version': __version__} - response = await client.post(app.url_path_for("check_version"), json=params) - assert response.status_code == status.HTTP_200_OK - assert response.json() == {'version': __version__} - - -async def test_version_invalid_input(app: FastAPI, client: AsyncClient) -> None: - - params = {'version': "0.4.2"} - response = await client.post(app.url_path_for("check_version"), json=params) - assert response.status_code == status.HTTP_409_CONFLICT - assert response.json() == {'message': 'Client version 0.4.2 is not the same as server version {}'.format(__version__)} - - -async def test_version_invalid_input_schema(app: FastAPI, client: AsyncClient) -> None: - - params = {'version': "0.4.2", "bla": "blu"} - response = await client.post(app.url_path_for("check_version"), json=params) - assert response.status_code == status.HTTP_409_CONFLICT - - -async def test_version_invalid_json(app: FastAPI, client: AsyncClient) -> None: - - params = "BOUM" - response = await client.post(app.url_path_for("check_version"), json=params) - assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + response = await client.get(app.url_path_for("get_version")) + assert response.status_code == status.HTTP_200_OK + assert response.json() == {'controller_host': '127.0.0.1', 'local': False, 'version': __version__} + + + async def test_version_input(self, app: FastAPI, client: AsyncClient) -> None: + + params = {'version': __version__} + response = await client.post(app.url_path_for("check_version"), json=params) + assert response.status_code == status.HTTP_200_OK + assert response.json() == {'version': __version__} + + + async def test_version_invalid_input(self, app: FastAPI, client: AsyncClient) -> None: + + params = {'version': "0.4.2"} + response = await client.post(app.url_path_for("check_version"), json=params) + assert response.status_code == status.HTTP_409_CONFLICT + assert response.json() == {'message': 'Client version 0.4.2 is not the same as server version {}'.format(__version__)} + + + async def test_version_invalid_input_schema(self, app: FastAPI, client: AsyncClient) -> None: + + params = {'version': "0.4.2", "bla": "blu"} + response = await client.post(app.url_path_for("check_version"), json=params) + assert response.status_code == status.HTTP_409_CONFLICT + + + async def test_version_invalid_json(self, app: FastAPI, client: AsyncClient) -> None: + + params = "BOUM" + response = await client.post(app.url_path_for("check_version"), json=params) + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY diff --git a/tests/api/routes/test_index.py b/tests/api/routes/test_index.py index 08dcc090..93cef6dd 100644 --- a/tests/api/routes/test_index.py +++ b/tests/api/routes/test_index.py @@ -29,55 +29,57 @@ from gns3server.utils.get_resource import get_resource pytestmark = pytest.mark.asyncio -def get_static(filename): +class TestIndexRoutes: - current_dir = os.path.dirname(os.path.abspath(__file__)) - return os.path.join(os.path.abspath(os.path.join(current_dir, '../..', '..', 'gns3server', 'static')), filename) + def get_static(self, filename): + + current_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(os.path.abspath(os.path.join(current_dir, '../..', '..', 'gns3server', 'static')), filename) -async def test_debug(app: FastAPI, client: AsyncClient) -> None: + async def test_debug(self, app: FastAPI, client: AsyncClient) -> None: - response = await client.get(app.url_path_for("debug")) - assert response.status_code == status.HTTP_200_OK - html = response.read().decode() - assert "Website" in html - assert __version__ in html - - -# @pytest.mark.asyncio -# async def test_controller(http_client, controller): -# -# await controller.add_project(name="test") -# response = await http_client.get('/controller') -# assert "test" in await response.text() -# assert response.status_code == 200 -# -# -# @pytest.mark.asyncio -# async def test_compute(http_client): -# -# response = await http_client.get('/compute') -# assert response.status_code == 200 - - -# @pytest.mark.asyncio -# async def test_project(http_client, controller): -# -# project = await controller.add_project(name="test") -# response = await http_client.get('/projects/{}'.format(project.id)) -# assert response.status_code == 200 - - -async def test_web_ui(app: FastAPI, client: AsyncClient) -> None: - - response = await client.get(app.url_path_for("web_ui", file_path="index.html")) - assert response.status_code == status.HTTP_200_OK - - -async def test_web_ui_not_found(app: FastAPI, client: AsyncClient, tmpdir: str) -> None: - - with patch('gns3server.utils.get_resource.get_resource') as mock: - mock.return_value = str(tmpdir) - response = await client.get(app.url_path_for("web_ui", file_path="not-found.txt")) - # should serve web-ui/index.html + response = await client.get(app.url_path_for("debug")) assert response.status_code == status.HTTP_200_OK + html = response.read().decode() + assert "Website" in html + assert __version__ in html + + + # @pytest.mark.asyncio + # async def test_controller(http_client, controller): + # + # await controller.add_project(name="test") + # response = await http_client.get('/controller') + # assert "test" in await response.text() + # assert response.status_code == 200 + # + # + # @pytest.mark.asyncio + # async def test_compute(http_client): + # + # response = await http_client.get('/compute') + # assert response.status_code == 200 + + + # @pytest.mark.asyncio + # async def test_project(http_client, controller): + # + # project = await controller.add_project(name="test") + # response = await http_client.get('/projects/{}'.format(project.id)) + # assert response.status_code == 200 + + + async def test_web_ui(self, app: FastAPI, client: AsyncClient) -> None: + + response = await client.get(app.url_path_for("web_ui", file_path="index.html")) + assert response.status_code == status.HTTP_200_OK + + + async def test_web_ui_not_found(self, app: FastAPI, client: AsyncClient, tmpdir: str) -> None: + + with patch('gns3server.utils.get_resource.get_resource') as mock: + mock.return_value = str(tmpdir) + response = await client.get(app.url_path_for("web_ui", file_path="not-found.txt")) + # should serve web-ui/index.html + assert response.status_code == status.HTTP_200_OK diff --git a/tests/api/routes/test_routes.py b/tests/api/routes/test_routes.py index 40870ee1..dc69cfca 100644 --- a/tests/api/routes/test_routes.py +++ b/tests/api/routes/test_routes.py @@ -44,40 +44,49 @@ ALLOWED_CONTROLLER_ENDPOINTS = [ ("/v3/symbols/default_symbols", "GET") ] +class TestRoutes: -# Controller endpoints have a OAuth2 bearer token authentication -async def test_controller_endpoints_require_authentication(app: FastAPI, unauthorized_client: AsyncClient) -> None: + # Controller endpoints have a OAuth2 bearer token authentication + async def test_controller_endpoints_require_authentication( + self, + app: FastAPI, + unauthorized_client: AsyncClient + ) -> None: - for route in app.routes: - if isinstance(route, APIRoute): - for method in list(route.methods): - if (route.path, method) not in ALLOWED_CONTROLLER_ENDPOINTS: - response = await getattr(unauthorized_client, method.lower())(route.path) - assert response.status_code == status.HTTP_401_UNAUTHORIZED - elif isinstance(route, APIWebSocketRoute): - params = {"token": "wrong_token"} - async with AsyncClient(base_url="http://test-api", transport=ASGIWebSocketTransport(app)) as client: - async with aconnect_ws(route.path, client, params=params) as ws: - json_notification = await ws.receive_json() - assert json_notification['event'] == { - 'message': 'Could not authenticate while connecting to controller WebSocket: Could not validate credentials' - } - - -# Compute endpoints have a basic HTTP authentication -async def test_compute_endpoints_require_authentication(app: FastAPI, unauthorized_client: AsyncClient) -> None: - - for route in app.routes: - if isinstance(route, Mount): - for compute_route in route.routes: - if isinstance(compute_route, APIRoute): - for method in list(compute_route.methods): - response = await getattr(unauthorized_client, method.lower())(route.path + compute_route.path) + for route in app.routes: + if isinstance(route, APIRoute): + for method in list(route.methods): + if (route.path, method) not in ALLOWED_CONTROLLER_ENDPOINTS: + response = await getattr(unauthorized_client, method.lower())(route.path) assert response.status_code == status.HTTP_401_UNAUTHORIZED - elif isinstance(compute_route, APIWebSocketRoute): - async with AsyncClient(base_url="http://test-api", transport=ASGIWebSocketTransport(app)) as client: - async with aconnect_ws(route.path + compute_route.path, client, auth=("wrong_user", "password123")) as ws: - json_notification = await ws.receive_json() - assert json_notification['event'] == { - 'message': 'Could not authenticate while connecting to compute WebSocket: Could not validate credentials' - } + elif isinstance(route, APIWebSocketRoute): + params = {"token": "wrong_token"} + async with AsyncClient(base_url="http://test-api", transport=ASGIWebSocketTransport(app=app)) as client: + async with aconnect_ws(route.path, client, params=params) as ws: + json_notification = await ws.receive_json() + assert json_notification['event'] == { + 'message': 'Could not authenticate while connecting to controller WebSocket: Could not validate credentials' + } + + + # Compute endpoints have a basic HTTP authentication + async def test_compute_endpoints_require_authentication( + self, + app: FastAPI, + unauthorized_client: AsyncClient + ) -> None: + + for route in app.routes: + if isinstance(route, Mount): + for compute_route in route.routes: + if isinstance(compute_route, APIRoute): + for method in list(compute_route.methods): + response = await getattr(unauthorized_client, method.lower())(route.path + compute_route.path) + assert response.status_code == status.HTTP_401_UNAUTHORIZED + elif isinstance(compute_route, APIWebSocketRoute): + async with AsyncClient(base_url="http://test-api", transport=ASGIWebSocketTransport(app=app)) as client: + async with aconnect_ws(route.path + compute_route.path, client, auth=("wrong_user", "password123")) as ws: + json_notification = await ws.receive_json() + assert json_notification['event'] == { + 'message': 'Could not authenticate while connecting to compute WebSocket: Could not validate credentials' + } diff --git a/tests/compute/builtin/nodes/test_cloud.py b/tests/compute/builtin/nodes/test_cloud.py index 7a482260..0f7d3401 100644 --- a/tests/compute/builtin/nodes/test_cloud.py +++ b/tests/compute/builtin/nodes/test_cloud.py @@ -25,6 +25,8 @@ from gns3server.compute.nios.nio_udp import NIOUDP from tests.utils import asyncio_patch +pytestmark = pytest.mark.asyncio + @pytest.fixture def nio(): @@ -39,7 +41,6 @@ async def manager(): return m -@pytest.mark.asyncio async def test_json_with_ports(on_gns3vm, compute_project, manager): ports = [ @@ -78,7 +79,7 @@ async def test_json_with_ports(on_gns3vm, compute_project, manager): } -def test_json_without_ports(on_gns3vm, compute_project, manager): +async def test_json_without_ports(on_gns3vm, compute_project, manager): """ If no interface is provide the cloud is pre-fill with non special interfaces """ @@ -117,7 +118,6 @@ def test_json_without_ports(on_gns3vm, compute_project, manager): } -@pytest.mark.asyncio async def test_update_port_mappings(on_gns3vm, compute_project): """ We don't allow an empty interface in the middle of port list @@ -158,7 +158,6 @@ async def test_update_port_mappings(on_gns3vm, compute_project): assert cloud.ports_mapping == ports1 -@pytest.mark.asyncio async def test_linux_ethernet_raw_add_nio(linux_platform, compute_project, nio): ports = [ { @@ -186,7 +185,6 @@ async def test_linux_ethernet_raw_add_nio(linux_platform, compute_project, nio): ]) -@pytest.mark.asyncio async def test_linux_ethernet_raw_add_nio_bridge(linux_platform, compute_project, nio): """ Bridge can't be connected directly to a cloud we use a tap in the middle diff --git a/tests/conftest.py b/tests/conftest.py index 30b3a6b0..0cf8be86 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,4 @@ import pytest -import asyncio import pytest_asyncio import tempfile import shutil @@ -13,6 +12,7 @@ import stat from fastapi import FastAPI from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from httpx import AsyncClient +from httpx_ws.transport import ASGIWebSocketTransport from unittest.mock import MagicMock, patch from pathlib import Path @@ -34,25 +34,14 @@ sys._called_from_test = True sys.original_platform = sys.platform -# https://github.com/pytest-dev/pytest-asyncio/issues/68 -# this event_loop is used by pytest-asyncio, and redefining it -# is currently the only way of changing the scope of this fixture -@pytest.fixture(scope="class") -def event_loop(request): - - loop = asyncio.get_event_loop_policy().new_event_loop() - yield loop - loop.close() - - -@pytest_asyncio.fixture(scope="class") +@pytest_asyncio.fixture(loop_scope="class", scope="class") async def app() -> FastAPI: from gns3server.api.server import app as gns3app yield gns3app -@pytest_asyncio.fixture(scope="class") +@pytest_asyncio.fixture(loop_scope="class", scope="class") async def db_engine(): db_url = os.getenv("GNS3_TEST_DATABASE_URI", "sqlite+aiosqlite:///:memory:") # "sqlite:///./sql_test_app.db" @@ -61,7 +50,7 @@ async def db_engine(): #await engine.sync_engine.dispose() -@pytest_asyncio.fixture(scope="class") +@pytest_asyncio.fixture(loop_scope="class", scope="class") async def db_session(db_engine): # recreate database tables for each class @@ -82,7 +71,7 @@ async def db_session(db_engine): await session.close() -@pytest_asyncio.fixture +@pytest_asyncio.fixture(loop_scope="class", scope="class") async def base_client(app: FastAPI, db_session: AsyncSession) -> AsyncClient: async def _get_test_db(): @@ -94,14 +83,14 @@ async def base_client(app: FastAPI, db_session: AsyncSession) -> AsyncClient: app.dependency_overrides[get_db_session] = _get_test_db async with AsyncClient( - app=app, base_url="http://test-api", - headers={"Content-Type": "application/json"} + headers={"Content-Type": "application/json"}, + transport=ASGIWebSocketTransport(app=app) ) as async_client: yield async_client -@pytest_asyncio.fixture +@pytest_asyncio.fixture(loop_scope="class", scope="class") async def test_user(db_session: AsyncSession) -> User: new_user = schemas.UserCreate( @@ -121,7 +110,7 @@ async def test_user(db_session: AsyncSession) -> User: return user -@pytest_asyncio.fixture +@pytest_asyncio.fixture(loop_scope="class", scope="class") async def test_compute(db_session: AsyncSession) -> Compute: new_compute = schemas.ComputeCreate( @@ -140,12 +129,12 @@ async def test_compute(db_session: AsyncSession) -> Compute: return await compute_repo.create_compute(new_compute) -@pytest.fixture +@pytest_asyncio.fixture(loop_scope="class", scope="class") def unauthorized_client(base_client: AsyncClient, test_user: User) -> AsyncClient: return base_client -@pytest.fixture +@pytest_asyncio.fixture(loop_scope="class", scope="class") def authorized_client(base_client: AsyncClient, test_user: User) -> AsyncClient: access_token = auth_service.create_access_token(test_user.username) @@ -156,7 +145,7 @@ def authorized_client(base_client: AsyncClient, test_user: User) -> AsyncClient: return base_client -@pytest_asyncio.fixture +@pytest_asyncio.fixture(loop_scope="class", scope="class") async def client(base_client: AsyncClient) -> AsyncClient: # The super admin is automatically created when the users table is created @@ -169,7 +158,7 @@ async def client(base_client: AsyncClient) -> AsyncClient: return base_client -@pytest_asyncio.fixture +@pytest_asyncio.fixture(loop_scope="class", scope="class") async def compute_client(base_client: AsyncClient) -> AsyncClient: # default compute username is 'gns3' diff --git a/tests/pytest.ini b/tests/pytest.ini new file mode 100644 index 00000000..88c111a5 --- /dev/null +++ b/tests/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +asyncio_default_fixture_loop_scope=function