Files
laser-data-hub/tests/test_entry_images.py
T
broccoliandClaude Fable 5 edfb216561 激光材料数据平台 1.3.0
公开数据看板、四位 Key 录入面板、管理后台与三级权限、动态字段配置、
PostgreSQL/SQLite 双支持、数据库备份,以及本版新增的成品图上传与预览。

图片相关:
- 录入表单支持相册选图与手机端直接拍照,每条记录最多 6 张
- 浏览器内压缩到最长边 1600 并剥离 EXIF,入库统一为 JPG/PNG
- 二进制直接入库,现有备份自动覆盖图片,部署无需新增卷
- 详情页缩略图宫格与全屏 lightbox,公开看板同样可见

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 22:06:02 +08:00

114 lines
5.7 KiB
Python

"""四位 Key 录入面板的图片链路:这是手机端最主要的入口,必须整条走通。"""
import hashlib
import hmac
import pytest
from conftest import png_bytes, record_payload
@pytest.fixture()
def entry(client, admin):
"""用管理员账号配好四位 Key,再换成录入会话。"""
from app.config import settings
from app.main import ENTRY_COOKIE
admin_client, admin_headers = admin
response = admin_client.put("/api/me/entry-key", headers=admin_headers, json={"key": "1379", "password": settings.admin_password})
assert response.status_code == 200, response.text
client.cookies.clear()
assert client.post("/api/entry/auth", json={"key": "1379"}).status_code == 200
token = client.cookies.get(ENTRY_COOKIE)
csrf = hmac.new(settings.secret.encode(), f"entry:{token}".encode(), hashlib.sha256).hexdigest()
return client, {"X-CSRF-Token": csrf}
def entry_upload(client, headers, **kwargs):
files = {"file": ("field.png", png_bytes(48, 36), "image/png"), "thumbnail": ("t.png", png_bytes(12, 9), "image/png")}
return client.post("/api/entry/uploads/images", headers=headers, files=files, **kwargs)
def test_entry_upload_requires_the_entry_session(client):
client.cookies.clear()
assert client.post("/api/entry/uploads/images", files={"file": ("a.png", png_bytes(), "image/png")}).status_code == 401
def test_entry_upload_requires_the_entry_csrf_token(entry):
client, _ = entry
assert client.post("/api/entry/uploads/images", files={"file": ("a.png", png_bytes(), "image/png")}).status_code == 403
def test_admin_csrf_token_is_not_accepted_on_the_entry_endpoint(entry, admin):
"""两套会话的 CSRF 派生方式不同,管理端令牌不能拿来打录入接口。"""
client, _ = entry
_, admin_headers = admin
assert client.post("/api/entry/uploads/images", headers=admin_headers, files={"file": ("a.png", png_bytes(), "image/png")}).status_code == 403
def test_entry_record_is_saved_with_its_images(entry):
from app.config import settings
client, headers = entry
token = entry_upload(client, headers).json()["data"]["token"]
payload = record_payload("LAS-ENTRY-001", image_tokens=[token], confirm_username=settings.admin_username)
created = client.post("/api/entry/records", headers=headers, json=payload)
assert created.status_code == 200, created.text
images = client.get(f"/api/public/records/{created.json()['data']['id']}").json()["data"]["images"]
assert len(images) == 1 and images[0]["width"] == 48
def test_entry_record_rejects_a_mismatched_username(entry):
client, headers = entry
token = entry_upload(client, headers).json()["data"]["token"]
payload = record_payload("LAS-ENTRY-002", image_tokens=[token], confirm_username="someone-else")
assert client.post("/api/entry/records", headers=headers, json=payload).status_code == 403
def test_entry_pending_image_can_be_discarded(entry):
client, headers = entry
token = entry_upload(client, headers).json()["data"]["token"]
assert client.delete(f"/api/entry/uploads/images/{token}", headers=headers).status_code == 200
assert client.delete(f"/api/entry/uploads/images/{token}", headers=headers).status_code == 404
def test_pending_image_of_another_account_cannot_be_claimed(client, admin):
"""令牌必须同时校验归属,否则拿到别人的令牌就能把图挂到自己的记录上。"""
from app.config import settings
from app.main import SESSION_COOKIE
admin_client, admin_headers = admin
created = admin_client.post("/api/users", headers=admin_headers, json={"username": "uploader9", "display_name": "录入九", "password": "UploaderPass123", "role": "uploader"})
assert created.status_code in (200, 409)
# 换成另一个账号上传,拿到属于它的令牌
client.cookies.clear()
client.post("/api/auth/login", data={"username": "uploader9", "password": "UploaderPass123"})
other_csrf = hmac.new(settings.secret.encode(), client.cookies.get(SESSION_COOKIE).encode(), hashlib.sha256).hexdigest()
stolen = client.post("/api/uploads/images", headers={"X-CSRF-Token": other_csrf}, files={"file": ("mine.png", png_bytes(20, 20), "image/png")}).json()["data"]["token"]
# 换回管理员,用别人的令牌建记录必须失败
client.cookies.clear()
client.post("/api/auth/login", data={"username": settings.admin_username, "password": settings.admin_password})
admin_csrf = hmac.new(settings.secret.encode(), client.cookies.get(SESSION_COOKIE).encode(), hashlib.sha256).hexdigest()
response = client.post("/api/records", headers={"X-CSRF-Token": admin_csrf}, json=record_payload("LAS-STEAL-001", image_tokens=[stolen]))
assert response.status_code == 404
def test_thumbnail_variant_reports_its_own_media_type(entry):
from app.config import settings
client, headers = entry
body = client.post("/api/entry/uploads/images", headers=headers, files={
"file": ("photo.jpg", open("/System/Library/CoreServices/DefaultBackground.jpg", "rb").read() if False else png_bytes(64, 64), "image/png"),
"thumbnail": ("t.png", png_bytes(16, 16), "image/png"),
}).json()
token = body["data"]["token"]
payload = record_payload("LAS-ENTRY-004", image_tokens=[token], confirm_username=settings.admin_username)
record_id = client.post("/api/entry/records", headers=headers, json=payload).json()["data"]["id"]
image = client.get(f"/api/public/records/{record_id}").json()["data"]["images"][0]
assert "variant=thumb" in image["thumb_url"]
response = client.get(image["thumb_url"])
assert response.status_code == 200 and response.headers["content-type"] == "image/png"
assert response.headers["etag"].endswith('-thumb"')