Files
wehub-resource-sync fed8b2eed7
Backend release / release (push) Waiting to run
Bandit Security Scan / bandit_scan (push) Waiting to run
Build and push multi-arch DocsGPT Docker image / build (linux/amd64, ubuntu-latest, amd64) (push) Waiting to run
Build and push multi-arch DocsGPT Docker image / build (linux/arm64, ubuntu-24.04-arm, arm64) (push) Waiting to run
Build and push multi-arch DocsGPT Docker image / manifest (push) Blocked by required conditions
Build and push DocsGPT FE Docker image for development / build (linux/amd64, ubuntu-latest, amd64) (push) Waiting to run
Build and push DocsGPT FE Docker image for development / build (linux/arm64, ubuntu-24.04-arm, arm64) (push) Waiting to run
Build and push DocsGPT FE Docker image for development / manifest (push) Blocked by required conditions
Python linting / ruff (push) Waiting to run
Run python tests with pytest / Run tests and count coverage (3.12) (push) Waiting to run
React Widget Build / build (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:28:29 +08:00

110 lines
3.3 KiB
Python

"""Tests for ``PATCH /api/devices/<id>`` body validation.
The validation rejects bad ``name``/``description`` values before any DB
access, so the 400 cases exercise the handler directly without Postgres.
A valid PATCH is covered with the repository + session mocked.
"""
from __future__ import annotations
import pytest
from flask import Flask
from application.api.devices import routes as routes_module
@pytest.fixture
def app():
return Flask(__name__)
def _call_update(app: Flask, body, device_id: str = "dev_abc"):
with app.test_request_context(
f"/api/devices/{device_id}", method="PATCH", json=body,
):
from flask import request as flask_request
flask_request.decoded_token = {"sub": "user_abc"}
return routes_module.update_device(device_id)
def test_patch_rejects_null_name(app):
response = _call_update(app, {"name": None})
assert response.status_code == 400
assert response.get_json()["error"] == "invalid_name"
def test_patch_rejects_blank_name(app):
response = _call_update(app, {"name": " "})
assert response.status_code == 400
assert response.get_json()["error"] == "invalid_name"
def test_patch_rejects_non_string_name(app):
response = _call_update(app, {"name": 123})
assert response.status_code == 400
assert response.get_json()["error"] == "invalid_name"
def test_patch_rejects_null_description(app):
response = _call_update(app, {"description": None})
assert response.status_code == 400
assert response.get_json()["error"] == "invalid_description"
def test_patch_rejects_non_string_description(app):
response = _call_update(app, {"description": ["x"]})
assert response.status_code == 400
assert response.get_json()["error"] == "invalid_description"
def test_patch_still_validates_approval_mode(app):
response = _call_update(app, {"approval_mode": "bogus"})
assert response.status_code == 400
assert response.get_json()["error"] == "invalid_approval_mode"
def test_patch_empty_body_is_no_fields(app):
response = _call_update(app, {})
assert response.status_code == 400
assert response.get_json()["error"] == "no_fields"
def test_patch_valid_name_trimmed_and_persisted(app, monkeypatch):
"""A valid PATCH trims the name and writes the stripped value."""
captured: dict = {}
class _Repo:
def __init__(self, _conn):
pass
def update(self, _device_id, _user_id, fields):
captured.update(fields)
return True
def get(self, device_id, user_id=None):
return {"id": device_id, "name": fields_name(), "description": ""}
def fields_name():
return captured.get("name")
class _Sess:
def __enter__(self):
return None
def __exit__(self, *a):
return False
monkeypatch.setattr(routes_module, "DevicesRepository", _Repo)
monkeypatch.setattr(routes_module, "db_session", _Sess)
monkeypatch.setattr(
"application.api.devices.pairing._upsert_remote_device_user_tool",
lambda conn, **kwargs: None,
)
response = _call_update(app, {"name": " laptop ", "description": " box "})
assert response.status_code == 200
assert captured["name"] == "laptop"
assert captured["description"] == "box"