forked from claw/flaskpaste
pastes: add display_name field
Authenticated users can tag pastes with a human-readable label via X-Display-Name header. Supports create, update, remove, and listing. Max 128 chars, control characters rejected.
This commit is contained in:
@@ -333,3 +333,97 @@ class TestPastesSearch:
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.data)
|
||||
assert data["count"] == 1
|
||||
|
||||
|
||||
class TestPastesDisplayName:
|
||||
"""Tests for display_name field in paste listing."""
|
||||
|
||||
def test_create_with_display_name(self, client, auth_header):
|
||||
"""Create paste with display_name returns it in response."""
|
||||
response = client.post(
|
||||
"/",
|
||||
data="test content",
|
||||
content_type="text/plain",
|
||||
headers={**auth_header, "X-Display-Name": "my notes"},
|
||||
)
|
||||
assert response.status_code == 201
|
||||
data = json.loads(response.data)
|
||||
assert data["display_name"] == "my notes"
|
||||
|
||||
def test_display_name_in_listing(self, client, auth_header):
|
||||
"""Display name appears in paste listing."""
|
||||
client.post(
|
||||
"/",
|
||||
data="test content",
|
||||
content_type="text/plain",
|
||||
headers={**auth_header, "X-Display-Name": "project docs"},
|
||||
)
|
||||
|
||||
response = client.get("/pastes", headers=auth_header)
|
||||
data = json.loads(response.data)
|
||||
assert data["count"] == 1
|
||||
assert data["pastes"][0]["display_name"] == "project docs"
|
||||
|
||||
def test_display_name_in_metadata(self, client, auth_header):
|
||||
"""Display name appears in single-paste GET."""
|
||||
create = client.post(
|
||||
"/",
|
||||
data="test content",
|
||||
content_type="text/plain",
|
||||
headers={**auth_header, "X-Display-Name": "readme"},
|
||||
)
|
||||
paste_id = json.loads(create.data)["id"]
|
||||
|
||||
response = client.get(f"/{paste_id}")
|
||||
data = json.loads(response.data)
|
||||
assert data["display_name"] == "readme"
|
||||
|
||||
def test_display_name_absent_when_unset(self, client, auth_header):
|
||||
"""Display name is omitted from response when not set."""
|
||||
create = client.post(
|
||||
"/",
|
||||
data="test content",
|
||||
content_type="text/plain",
|
||||
headers=auth_header,
|
||||
)
|
||||
paste_id = json.loads(create.data)["id"]
|
||||
|
||||
response = client.get(f"/{paste_id}")
|
||||
data = json.loads(response.data)
|
||||
assert "display_name" not in data
|
||||
|
||||
def test_anonymous_display_name_ignored(self, client):
|
||||
"""Anonymous user's display_name header is silently ignored."""
|
||||
create = client.post(
|
||||
"/",
|
||||
data="test content",
|
||||
content_type="text/plain",
|
||||
headers={"X-Display-Name": "sneaky"},
|
||||
)
|
||||
assert create.status_code == 201
|
||||
data = json.loads(create.data)
|
||||
assert "display_name" not in data
|
||||
|
||||
def test_display_name_too_long(self, client, auth_header):
|
||||
"""Display name exceeding 128 chars is rejected."""
|
||||
response = client.post(
|
||||
"/",
|
||||
data="test content",
|
||||
content_type="text/plain",
|
||||
headers={**auth_header, "X-Display-Name": "a" * 129},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
data = json.loads(response.data)
|
||||
assert "too long" in data["error"].lower()
|
||||
|
||||
def test_display_name_control_chars_rejected(self, client, auth_header):
|
||||
"""Display name with control characters is rejected."""
|
||||
response = client.post(
|
||||
"/",
|
||||
data="test content",
|
||||
content_type="text/plain",
|
||||
headers={**auth_header, "X-Display-Name": "bad\x00name"},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
data = json.loads(response.data)
|
||||
assert "invalid" in data["error"].lower()
|
||||
|
||||
@@ -202,6 +202,86 @@ class TestPasteUpdateEndpoint:
|
||||
assert data.get("password_protected") is True
|
||||
|
||||
|
||||
class TestPasteUpdateDisplayName:
|
||||
"""Tests for display_name update via PUT."""
|
||||
|
||||
def test_update_set_display_name(self, client, auth_header):
|
||||
"""Set display_name on existing paste."""
|
||||
create = client.post("/", data="content", content_type="text/plain", headers=auth_header)
|
||||
paste_id = json.loads(create.data)["id"]
|
||||
|
||||
response = client.put(
|
||||
f"/{paste_id}",
|
||||
data="",
|
||||
headers={**auth_header, "X-Display-Name": "my label"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.data)
|
||||
assert data["display_name"] == "my label"
|
||||
|
||||
def test_update_change_display_name(self, client, auth_header):
|
||||
"""Change existing display_name."""
|
||||
create = client.post(
|
||||
"/",
|
||||
data="content",
|
||||
content_type="text/plain",
|
||||
headers={**auth_header, "X-Display-Name": "old name"},
|
||||
)
|
||||
paste_id = json.loads(create.data)["id"]
|
||||
|
||||
response = client.put(
|
||||
f"/{paste_id}",
|
||||
data="",
|
||||
headers={**auth_header, "X-Display-Name": "new name"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.data)
|
||||
assert data["display_name"] == "new name"
|
||||
|
||||
def test_update_remove_display_name(self, client, auth_header):
|
||||
"""Remove display_name via X-Remove-Display-Name header."""
|
||||
create = client.post(
|
||||
"/",
|
||||
data="content",
|
||||
content_type="text/plain",
|
||||
headers={**auth_header, "X-Display-Name": "to remove"},
|
||||
)
|
||||
paste_id = json.loads(create.data)["id"]
|
||||
|
||||
response = client.put(
|
||||
f"/{paste_id}",
|
||||
data="",
|
||||
headers={**auth_header, "X-Remove-Display-Name": "true"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.data)
|
||||
assert "display_name" not in data
|
||||
|
||||
def test_update_display_name_too_long(self, client, auth_header):
|
||||
"""Reject display_name exceeding 128 chars in update."""
|
||||
create = client.post("/", data="content", content_type="text/plain", headers=auth_header)
|
||||
paste_id = json.loads(create.data)["id"]
|
||||
|
||||
response = client.put(
|
||||
f"/{paste_id}",
|
||||
data="",
|
||||
headers={**auth_header, "X-Display-Name": "x" * 129},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
|
||||
def test_update_display_name_control_chars(self, client, auth_header):
|
||||
"""Reject display_name with control characters in update."""
|
||||
create = client.post("/", data="content", content_type="text/plain", headers=auth_header)
|
||||
paste_id = json.loads(create.data)["id"]
|
||||
|
||||
response = client.put(
|
||||
f"/{paste_id}",
|
||||
data="",
|
||||
headers={**auth_header, "X-Display-Name": "bad\x01name"},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
class TestPasteUpdatePrivacy:
|
||||
"""Privacy-focused tests for paste update."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user