mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-10-02 08:40:15 +03:00
Merge branch '3.0' into feature/ai-copilot-bridge
This commit is contained in:
commit
9aa3f3b7a0
@ -154,6 +154,17 @@ async def upload_image(
|
|||||||
if os.path.commonprefix([base_images_directory, full_path]) != base_images_directory:
|
if os.path.commonprefix([base_images_directory, full_path]) != base_images_directory:
|
||||||
raise ControllerForbiddenError(f"Cannot write image, '{image_path}' is forbidden")
|
raise ControllerForbiddenError(f"Cannot write image, '{image_path}' is forbidden")
|
||||||
|
|
||||||
|
# If the client sends X-MD5-Checksum, check for a duplicate before consuming the upload stream
|
||||||
|
checksum_header = request.headers.get("X-MD5-Checksum")
|
||||||
|
if checksum_header:
|
||||||
|
check_dir = os.path.dirname(full_path) if image_dir else None
|
||||||
|
duplicate = await images_repo.get_image_by_checksum(checksum_header, check_dir)
|
||||||
|
if duplicate:
|
||||||
|
location = f" in '{check_dir}'" if check_dir else ""
|
||||||
|
raise ControllerError(
|
||||||
|
f"Image '{duplicate.filename}' with the same checksum already exists{location}"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
allow_raw_image = Config.instance().settings.Server.allow_raw_images
|
allow_raw_image = Config.instance().settings.Server.allow_raw_images
|
||||||
image = await write_image(image_path, full_path, request.stream(), images_repo, allow_raw_image=allow_raw_image)
|
image = await write_image(image_path, full_path, request.stream(), images_repo, allow_raw_image=allow_raw_image)
|
||||||
|
|||||||
@ -105,6 +105,16 @@ async def dep_node(node_id: UUID, project: Project = Depends(dep_project)) -> No
|
|||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
def _check_node_type(node: Node, *required_types: str) -> None:
|
||||||
|
"""
|
||||||
|
Raise ControllerBadRequestError if node is not one of the required types.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if node.node_type not in required_types:
|
||||||
|
type_str = "/".join(required_types)
|
||||||
|
raise ControllerBadRequestError(f"This endpoint is only supported on a {type_str} node")
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"",
|
"",
|
||||||
status_code=status.HTTP_201_CREATED,
|
status_code=status.HTTP_201_CREATED,
|
||||||
@ -407,8 +417,7 @@ async def auto_idlepc(node: Node = Depends(dep_node)) -> dict:
|
|||||||
Required privilege: Node.Audit
|
Required privilege: Node.Audit
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if node.node_type != "dynamips":
|
_check_node_type(node, "dynamips")
|
||||||
raise ControllerBadRequestError("Auto Idle-PC is only supported on a Dynamips node")
|
|
||||||
return await node.dynamips_auto_idlepc()
|
return await node.dynamips_auto_idlepc()
|
||||||
|
|
||||||
|
|
||||||
@ -420,8 +429,7 @@ async def idlepc_proposals(node: Node = Depends(dep_node)) -> List[str]:
|
|||||||
Required privilege: Node.Audit
|
Required privilege: Node.Audit
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if node.node_type != "dynamips":
|
_check_node_type(node, "dynamips")
|
||||||
raise ControllerBadRequestError("Idle-PC proposals is only supported on a Dynamips node")
|
|
||||||
return await node.dynamips_idlepc_proposals()
|
return await node.dynamips_idlepc_proposals()
|
||||||
|
|
||||||
|
|
||||||
@ -441,8 +449,7 @@ async def create_disk_image(
|
|||||||
Required privilege: Node.Allocate
|
Required privilege: Node.Allocate
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if node.node_type != "qemu":
|
_check_node_type(node, "qemu")
|
||||||
raise ControllerBadRequestError("Creating a disk image is only supported on a Qemu node")
|
|
||||||
await node.post(f"/disk_image/{disk_name}", data=disk_data.model_dump(exclude_unset=True))
|
await node.post(f"/disk_image/{disk_name}", data=disk_data.model_dump(exclude_unset=True))
|
||||||
|
|
||||||
|
|
||||||
@ -462,8 +469,7 @@ async def update_disk_image(
|
|||||||
Required privilege: Node.Allocate
|
Required privilege: Node.Allocate
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if node.node_type != "qemu":
|
_check_node_type(node, "qemu")
|
||||||
raise ControllerBadRequestError("Updating a disk image is only supported on a Qemu node")
|
|
||||||
await node.put(f"/disk_image/{disk_name}", data=disk_data.model_dump(exclude_unset=True))
|
await node.put(f"/disk_image/{disk_name}", data=disk_data.model_dump(exclude_unset=True))
|
||||||
|
|
||||||
|
|
||||||
@ -482,8 +488,7 @@ async def delete_disk_image(
|
|||||||
Required privilege: Node.Allocate
|
Required privilege: Node.Allocate
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if node.node_type != "qemu":
|
_check_node_type(node, "qemu")
|
||||||
raise ControllerBadRequestError("Deleting a disk image is only supported on a Qemu node")
|
|
||||||
await node.delete(f"/disk_image/{disk_name}")
|
await node.delete(f"/disk_image/{disk_name}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -207,7 +207,7 @@ async def sqlalchemy_error_handler(request: Request, exc: SQLAlchemyError):
|
|||||||
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||||
log.error(f"Request validation error in {request.url.path} ({request.method}): {exc}")
|
log.error(f"Request validation error in {request.url.path} ({request.method}): {exc}")
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||||
content={"message": str(exc)}
|
content={"message": str(exc)}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -289,7 +289,7 @@ def _export_local_image(image, zstream):
|
|||||||
# Some modules don't have images
|
# Some modules don't have images
|
||||||
continue
|
continue
|
||||||
|
|
||||||
directory = os.path.split(images_directory)[-1:][0]
|
directory = os.path.basename(images_directory)
|
||||||
if os.path.exists(image):
|
if os.path.exists(image):
|
||||||
path = image
|
path = image
|
||||||
else:
|
else:
|
||||||
@ -309,7 +309,7 @@ async def _export_remote_images(project, compute_id, image_type, image, project_
|
|||||||
|
|
||||||
log.debug(f"Downloading image '{image}' from compute '{compute_id}'")
|
log.debug(f"Downloading image '{image}' from compute '{compute_id}'")
|
||||||
try:
|
try:
|
||||||
compute = [compute for compute in project.computes if compute.id == compute_id][0]
|
compute = next(c for c in project.computes if c.id == compute_id)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise ControllerNotFoundError(f"Cannot export image from '{compute_id}' compute. Compute doesn't exist.")
|
raise ControllerNotFoundError(f"Cannot export image from '{compute_id}' compute. Compute doesn't exist.")
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@ from .controller_error import (
|
|||||||
ComputeError,
|
ComputeError,
|
||||||
ComputeConflictError
|
ComputeConflictError
|
||||||
)
|
)
|
||||||
|
from .node_types import BUILTIN_NODE_TYPES
|
||||||
from .ports.port_factory import PortFactory, StandardPortFactory, DynamipsPortFactory
|
from .ports.port_factory import PortFactory, StandardPortFactory, DynamipsPortFactory
|
||||||
from ..utils.images import images_directories
|
from ..utils.images import images_directories
|
||||||
from ..utils import macaddress_to_int, int_to_macaddress
|
from ..utils import macaddress_to_int, int_to_macaddress
|
||||||
@ -452,8 +453,7 @@ class Node:
|
|||||||
if (
|
if (
|
||||||
prop == "name"
|
prop == "name"
|
||||||
and self.status == "started"
|
and self.status == "started"
|
||||||
and self._node_type
|
and self._node_type not in BUILTIN_NODE_TYPES
|
||||||
not in ("cloud", "nat", "ethernet_switch", "ethernet_hub", "frame_relay_switch", "atm_switch")
|
|
||||||
):
|
):
|
||||||
raise ControllerError("Sorry, it is not possible to rename a node that is already powered on")
|
raise ControllerError("Sorry, it is not possible to rename a node that is already powered on")
|
||||||
setattr(self, prop, kwargs[prop])
|
setattr(self, prop, kwargs[prop])
|
||||||
@ -541,35 +541,22 @@ class Node:
|
|||||||
if self._console:
|
if self._console:
|
||||||
# console is optional for builtin nodes
|
# console is optional for builtin nodes
|
||||||
data["console"] = self._console
|
data["console"] = self._console
|
||||||
if self._console_type and self._node_type not in (
|
if self._console_type and self._node_type not in (BUILTIN_NODE_TYPES - {"ethernet_switch"}):
|
||||||
"cloud",
|
|
||||||
"nat",
|
|
||||||
"ethernet_hub",
|
|
||||||
"frame_relay_switch",
|
|
||||||
"atm_switch",
|
|
||||||
):
|
|
||||||
# console_type is not supported by all builtin nodes excepting Ethernet switch
|
# console_type is not supported by all builtin nodes excepting Ethernet switch
|
||||||
data["console_type"] = self._console_type
|
data["console_type"] = self._console_type
|
||||||
if self._aux:
|
if self._aux:
|
||||||
# aux is optional for builtin nodes
|
# aux is optional for builtin nodes
|
||||||
data["aux"] = self._aux
|
data["aux"] = self._aux
|
||||||
if self._aux_type and self._node_type not in (
|
if self._aux_type and self._node_type not in BUILTIN_NODE_TYPES:
|
||||||
"cloud",
|
|
||||||
"nat",
|
|
||||||
"ethernet_switch",
|
|
||||||
"ethernet_hub",
|
|
||||||
"frame_relay_switch",
|
|
||||||
"atm_switch",
|
|
||||||
):
|
|
||||||
# aux_type is not supported by all builtin nodes
|
# aux_type is not supported by all builtin nodes
|
||||||
data["aux_type"] = self._aux_type
|
data["aux_type"] = self._aux_type
|
||||||
if self.custom_adapters:
|
if self.custom_adapters:
|
||||||
data["custom_adapters"] = self.custom_adapters
|
data["custom_adapters"] = self.custom_adapters
|
||||||
|
|
||||||
# None properties are not be sent because it can mean the emulator doesn't support it
|
# None properties are not be sent because it can mean the emulator doesn't support it
|
||||||
for key in list(data.keys()):
|
for key, value in list(data.items()):
|
||||||
if data[key] is None or data[key] == {} or key in self.CONTROLLER_ONLY_PROPERTIES:
|
if value is None or value == {} or key in self.CONTROLLER_ONLY_PROPERTIES:
|
||||||
del data[key]
|
del value
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|||||||
31
gns3server/controller/node_types.py
Normal file
31
gns3server/controller/node_types.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright (C) 2026 GNS3 Technologies Inc.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Shared node type constants used across the controller.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Node types that are always running (builtin/virtual switch nodes).
|
||||||
|
# These nodes do not require explicit start/stop and have limited feature support.
|
||||||
|
BUILTIN_NODE_TYPES = frozenset({
|
||||||
|
"cloud",
|
||||||
|
"nat",
|
||||||
|
"ethernet_switch",
|
||||||
|
"ethernet_hub",
|
||||||
|
"frame_relay_switch",
|
||||||
|
"atm_switch",
|
||||||
|
})
|
||||||
@ -175,7 +175,8 @@ def load_topology(path):
|
|||||||
# Version GNS3 2.2 dev (for project created with 2.2dev).
|
# Version GNS3 2.2 dev (for project created with 2.2dev).
|
||||||
# Appliance ID has been replaced by Template ID
|
# Appliance ID has been replaced by Template ID
|
||||||
if topo["revision"] == 9:
|
if topo["revision"] == 9:
|
||||||
for node in topo.get("topology", {}).get("nodes", []):
|
nodes = topo.get("topology", {}).get("nodes", [])
|
||||||
|
for node in nodes:
|
||||||
if "appliance_id" in node:
|
if "appliance_id" in node:
|
||||||
node["template_id"] = node["appliance_id"]
|
node["template_id"] = node["appliance_id"]
|
||||||
del node["appliance_id"]
|
del node["appliance_id"]
|
||||||
@ -218,7 +219,8 @@ def _convert_2_2_0(topo, topo_path):
|
|||||||
|
|
||||||
topo["revision"] = 10
|
topo["revision"] = 10
|
||||||
|
|
||||||
for node in topo.get("topology", {}).get("nodes", []):
|
nodes = topo.get("topology", {}).get("nodes", [])
|
||||||
|
for node in nodes:
|
||||||
if "properties" in node:
|
if "properties" in node:
|
||||||
if node["node_type"] in ("qemu", "docker") and not is_rfc1123_hostname_valid(node["name"]):
|
if node["node_type"] in ("qemu", "docker") and not is_rfc1123_hostname_valid(node["name"]):
|
||||||
new_name = to_rfc1123_hostname(node["name"])
|
new_name = to_rfc1123_hostname(node["name"])
|
||||||
@ -246,7 +248,8 @@ def _convert_2_1_0(topo, topo_path):
|
|||||||
# to avoid overlapping grids
|
# to avoid overlapping grids
|
||||||
topo["drawing_grid_size"] = topo["grid_size"]
|
topo["drawing_grid_size"] = topo["grid_size"]
|
||||||
|
|
||||||
for node in topo.get("topology", {}).get("nodes", []):
|
nodes = topo.get("topology", {}).get("nodes", [])
|
||||||
|
for node in nodes:
|
||||||
# make sure console_type is not None but "none" string
|
# make sure console_type is not None but "none" string
|
||||||
if "console_type" in node and node["console_type"] is None:
|
if "console_type" in node and node["console_type"] is None:
|
||||||
node["console_type"] = "none"
|
node["console_type"] = "none"
|
||||||
@ -272,7 +275,8 @@ def _convert_2_0_0(topo, topo_path):
|
|||||||
"""
|
"""
|
||||||
topo["revision"] = 8
|
topo["revision"] = 8
|
||||||
|
|
||||||
for node in topo.get("topology", {}).get("nodes", []):
|
nodes = topo.get("topology", {}).get("nodes", [])
|
||||||
|
for node in nodes:
|
||||||
if "properties" in node:
|
if "properties" in node:
|
||||||
if node["node_type"] == "vpcs":
|
if node["node_type"] == "vpcs":
|
||||||
if "startup_script_path" in node["properties"]:
|
if "startup_script_path" in node["properties"]:
|
||||||
@ -301,7 +305,8 @@ def _convert_2_0_0_beta_2(topo, topo_path):
|
|||||||
topo_dir = os.path.dirname(topo_path)
|
topo_dir = os.path.dirname(topo_path)
|
||||||
topo["revision"] = 7
|
topo["revision"] = 7
|
||||||
|
|
||||||
for node in topo.get("topology", {}).get("nodes", []):
|
nodes = topo.get("topology", {}).get("nodes", [])
|
||||||
|
for node in nodes:
|
||||||
if node["node_type"] == "dynamips":
|
if node["node_type"] == "dynamips":
|
||||||
node_id = node["node_id"]
|
node_id = node["node_id"]
|
||||||
dynamips_id = node["properties"]["dynamips_id"]
|
dynamips_id = node["properties"]["dynamips_id"]
|
||||||
@ -328,7 +333,8 @@ def _convert_2_0_0_alpha(topo, topo_path):
|
|||||||
* No more option for VMware / VirtualBox remote console (always use telnet)
|
* No more option for VMware / VirtualBox remote console (always use telnet)
|
||||||
"""
|
"""
|
||||||
topo["revision"] = 6
|
topo["revision"] = 6
|
||||||
for node in topo.get("topology", {}).get("nodes", []):
|
nodes = topo.get("topology", {}).get("nodes", [])
|
||||||
|
for node in nodes:
|
||||||
if node.get("console_type") == "serial":
|
if node.get("console_type") == "serial":
|
||||||
node["console_type"] = "telnet"
|
node["console_type"] = "telnet"
|
||||||
if node["node_type"] in ("vmware", "virtualbox"):
|
if node["node_type"] in ("vmware", "virtualbox"):
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
from .controller_error import ControllerError, ControllerNotFoundError
|
from .controller_error import ControllerError, ControllerNotFoundError
|
||||||
from .link import Link
|
from .link import Link
|
||||||
|
from .node_types import BUILTIN_NODE_TYPES
|
||||||
|
|
||||||
|
|
||||||
class UDPLink(Link):
|
class UDPLink(Link):
|
||||||
@ -32,6 +33,18 @@ class UDPLink(Link):
|
|||||||
Use for the debug exports
|
Use for the debug exports
|
||||||
"""
|
"""
|
||||||
return self._link_data
|
return self._link_data
|
||||||
|
|
||||||
|
def _get_node_filters(self, node1, node2):
|
||||||
|
"""
|
||||||
|
Determine which node gets the active filters applied.
|
||||||
|
|
||||||
|
:returns: Tuple of (node1_filters, node2_filters)
|
||||||
|
"""
|
||||||
|
filter_node = self._get_filter_node()
|
||||||
|
return (
|
||||||
|
self.get_active_filters() if filter_node == node1 else {},
|
||||||
|
self.get_active_filters() if filter_node == node2 else {},
|
||||||
|
)
|
||||||
|
|
||||||
async def create(self):
|
async def create(self):
|
||||||
"""
|
"""
|
||||||
@ -57,13 +70,7 @@ class UDPLink(Link):
|
|||||||
response = await node2.compute.post(f"/projects/{self._project.id}/ports/udp")
|
response = await node2.compute.post(f"/projects/{self._project.id}/ports/udp")
|
||||||
self._node2_port = response.json["udp_port"]
|
self._node2_port = response.json["udp_port"]
|
||||||
|
|
||||||
node1_filters = {}
|
node1_filters, node2_filters = self._get_node_filters(node1, node2)
|
||||||
node2_filters = {}
|
|
||||||
filter_node = self._get_filter_node()
|
|
||||||
if filter_node == node1:
|
|
||||||
node1_filters = self.get_active_filters()
|
|
||||||
elif filter_node == node2:
|
|
||||||
node2_filters = self.get_active_filters()
|
|
||||||
|
|
||||||
# Create the tunnel on both side
|
# Create the tunnel on both side
|
||||||
self._link_data.append(
|
self._link_data.append(
|
||||||
@ -108,13 +115,7 @@ class UDPLink(Link):
|
|||||||
node1 = self._nodes[0]["node"]
|
node1 = self._nodes[0]["node"]
|
||||||
node2 = self._nodes[1]["node"]
|
node2 = self._nodes[1]["node"]
|
||||||
|
|
||||||
node1_filters = {}
|
node1_filters, node2_filters = self._get_node_filters(node1, node2)
|
||||||
node2_filters = {}
|
|
||||||
filter_node = self._get_filter_node()
|
|
||||||
if filter_node == node1:
|
|
||||||
node1_filters = self.get_active_filters()
|
|
||||||
elif filter_node == node2:
|
|
||||||
node2_filters = self.get_active_filters()
|
|
||||||
|
|
||||||
adapter_number1 = self._nodes[0]["adapter_number"]
|
adapter_number1 = self._nodes[0]["adapter_number"]
|
||||||
port_number1 = self._nodes[0]["port_number"]
|
port_number1 = self._nodes[0]["port_number"]
|
||||||
@ -213,18 +214,16 @@ class UDPLink(Link):
|
|||||||
:returns: Node where the capture should run
|
:returns: Node where the capture should run
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ALWAYS_RUNNING_NODES_TYPE = ("cloud", "nat", "ethernet_switch", "ethernet_hub", "frame_relay_switch", "atm_switch")
|
|
||||||
|
|
||||||
for node in self._nodes:
|
for node in self._nodes:
|
||||||
if (
|
if (
|
||||||
node["node"].compute.id == "local"
|
node["node"].compute.id == "local"
|
||||||
and node["node"].node_type in ALWAYS_RUNNING_NODES_TYPE
|
and node["node"].node_type in BUILTIN_NODE_TYPES
|
||||||
and node["node"].status == "started"
|
and node["node"].status == "started"
|
||||||
):
|
):
|
||||||
return node
|
return node
|
||||||
|
|
||||||
for node in self._nodes:
|
for node in self._nodes:
|
||||||
if node["node"].node_type in ALWAYS_RUNNING_NODES_TYPE and node["node"].status == "started":
|
if node["node"].node_type in BUILTIN_NODE_TYPES and node["node"].status == "started":
|
||||||
return node
|
return node
|
||||||
|
|
||||||
for node in self._nodes:
|
for node in self._nodes:
|
||||||
|
|||||||
@ -297,7 +297,7 @@ class TestEthernetSwitchNodesRoutes:
|
|||||||
node_id=ethernet_switch["node_id"]),
|
node_id=ethernet_switch["node_id"]),
|
||||||
json=port_params
|
json=port_params
|
||||||
)
|
)
|
||||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
assert response.status_code == status.HTTP_422_UNPROCESSABLE_CONTENT
|
||||||
|
|
||||||
|
|
||||||
async def test_ethernet_switch_delete(
|
async def test_ethernet_switch_delete(
|
||||||
|
|||||||
@ -133,7 +133,7 @@ class TestTemplateRoutes:
|
|||||||
"template_type": "invalid_template_type"}
|
"template_type": "invalid_template_type"}
|
||||||
|
|
||||||
response = await client.post(app.url_path_for("create_template"), json=params)
|
response = await client.post(app.url_path_for("create_template"), json=params)
|
||||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
assert response.status_code == status.HTTP_422_UNPROCESSABLE_CONTENT
|
||||||
|
|
||||||
async def test_template_update(self, app: FastAPI, client: AsyncClient) -> None:
|
async def test_template_update(self, app: FastAPI, client: AsyncClient) -> None:
|
||||||
|
|
||||||
|
|||||||
@ -75,10 +75,10 @@ class TestUserRoutes:
|
|||||||
(
|
(
|
||||||
("email", "user2@email.com", status.HTTP_400_BAD_REQUEST),
|
("email", "user2@email.com", status.HTTP_400_BAD_REQUEST),
|
||||||
("username", "user2", status.HTTP_400_BAD_REQUEST),
|
("username", "user2", status.HTTP_400_BAD_REQUEST),
|
||||||
("email", "invalid_email@one@two.io", status.HTTP_422_UNPROCESSABLE_ENTITY),
|
("email", "invalid_email@one@two.io", status.HTTP_422_UNPROCESSABLE_CONTENT),
|
||||||
("password", "short", status.HTTP_422_UNPROCESSABLE_ENTITY),
|
("password", "short", status.HTTP_422_UNPROCESSABLE_CONTENT),
|
||||||
("username", "user2@#$%^<>", status.HTTP_422_UNPROCESSABLE_ENTITY),
|
("username", "user2@#$%^<>", status.HTTP_422_UNPROCESSABLE_CONTENT),
|
||||||
("username", "ab", status.HTTP_422_UNPROCESSABLE_ENTITY),
|
("username", "ab", status.HTTP_422_UNPROCESSABLE_CONTENT),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
async def test_user_registration_fails_when_credentials_are_taken(
|
async def test_user_registration_fails_when_credentials_are_taken(
|
||||||
@ -101,10 +101,10 @@ class TestUserRoutes:
|
|||||||
("email", "user@email.com", status.HTTP_200_OK),
|
("email", "user@email.com", status.HTTP_200_OK),
|
||||||
("email", "user@email.com", status.HTTP_400_BAD_REQUEST),
|
("email", "user@email.com", status.HTTP_400_BAD_REQUEST),
|
||||||
("username", "user2", status.HTTP_400_BAD_REQUEST),
|
("username", "user2", status.HTTP_400_BAD_REQUEST),
|
||||||
("email", "invalid_email@one@two.io", status.HTTP_422_UNPROCESSABLE_ENTITY),
|
("email", "invalid_email@one@two.io", status.HTTP_422_UNPROCESSABLE_CONTENT),
|
||||||
("password", "short", status.HTTP_422_UNPROCESSABLE_ENTITY),
|
("password", "short", status.HTTP_422_UNPROCESSABLE_CONTENT),
|
||||||
("username", "user2@#$%^<>", status.HTTP_422_UNPROCESSABLE_ENTITY),
|
("username", "user2@#$%^<>", status.HTTP_422_UNPROCESSABLE_CONTENT),
|
||||||
("username", "ab", status.HTTP_422_UNPROCESSABLE_ENTITY),
|
("username", "ab", status.HTTP_422_UNPROCESSABLE_CONTENT),
|
||||||
("full_name", "John Doe", status.HTTP_200_OK),
|
("full_name", "John Doe", status.HTTP_200_OK),
|
||||||
("password", "password123", status.HTTP_200_OK),
|
("password", "password123", status.HTTP_200_OK),
|
||||||
("is_active", True, status.HTTP_200_OK),
|
("is_active", True, status.HTTP_200_OK),
|
||||||
@ -259,7 +259,7 @@ class TestUserLogin:
|
|||||||
(
|
(
|
||||||
("wrong_username", "user1_password", status.HTTP_401_UNAUTHORIZED),
|
("wrong_username", "user1_password", status.HTTP_401_UNAUTHORIZED),
|
||||||
("user1", "wrong_password", status.HTTP_401_UNAUTHORIZED),
|
("user1", "wrong_password", status.HTTP_401_UNAUTHORIZED),
|
||||||
("user1", None, status.HTTP_422_UNPROCESSABLE_ENTITY),
|
("user1", None, status.HTTP_422_UNPROCESSABLE_CONTENT),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def test_user_with_wrong_creds_doesnt_receive_token(
|
async def test_user_with_wrong_creds_doesnt_receive_token(
|
||||||
|
|||||||
@ -62,4 +62,4 @@ class TestVersionRoutes:
|
|||||||
|
|
||||||
params = "BOUM"
|
params = "BOUM"
|
||||||
response = await client.post(app.url_path_for("check_version"), json=params)
|
response = await client.post(app.url_path_for("check_version"), json=params)
|
||||||
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
assert response.status_code == status.HTTP_422_UNPROCESSABLE_CONTENT
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user