# -*- coding: utf-8 -*- """ 文件上传测试。POST /api/upload 上传图片,DELETE /api/upload 删除。 验证:本地存储(OSS 未配置时)、响应格式、删除流程。 """ import io import pytest import requests # 最小有效 JPEG(1x1 像素,约 100 字节) _MIN_JPEG = ( b"\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00" b"\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f\x14\x1d\x1a\x1f\x1e\x1d\x1a\x1c\x1c" b" $.' \",#\x1c\x1c(7),01444\x1f'9=82<.342\xff\xc0\x00\x0b\x08\x00\x01\x00\x01\x01\x01\x11\x00\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xda\x00\x08\x01\x01\x00\x00\x00?\x00\xfe\x02\x1f\xff\xd9" ) def test_upload_post_success(base_url): """POST /api/upload 上传图片成功,返回 url、fileName、size、type""" files = {"file": ("test_upload.jpg", io.BytesIO(_MIN_JPEG), "image/jpeg")} data = {"folder": "test"} r = requests.post( f"{base_url}/api/upload", files=files, data=data, timeout=10, ) assert r.status_code == 200, f"期望 200,实际 {r.status_code}: {r.text}" body = r.json() assert body.get("success") is True, body assert "url" in body, body assert body["url"], "url 不应为空" data_out = body.get("data", {}) assert "url" in data_out assert "fileName" in data_out assert "size" in data_out assert data_out["size"] == len(_MIN_JPEG) assert "type" in data_out assert "image" in str(data_out.get("type", "")).lower() def test_upload_post_with_admin_token(base_url, admin_token): """POST /api/upload 带管理端 token 也可上传(接口不强制鉴权)""" if not admin_token: pytest.skip("admin 登录失败") files = {"file": ("avatar.jpg", io.BytesIO(_MIN_JPEG), "image/jpeg")} # multipart 上传不设 Content-Type,让 requests 自动带 boundary headers = {"Authorization": f"Bearer {admin_token}"} r = requests.post( f"{base_url}/api/upload", files=files, headers=headers, timeout=10, ) assert r.status_code == 200 body = r.json() assert body.get("success") is True assert body.get("url") def test_upload_post_no_file(base_url): """POST /api/upload 无 file 返回 400""" r = requests.post( f"{base_url}/api/upload", data={"folder": "test"}, timeout=10, ) assert r.status_code == 400 body = r.json() assert body.get("success") is False assert "error" in body or "请选择" in body.get("error", "") def test_upload_post_invalid_type(base_url): """POST /api/upload 非图片格式返回 400""" files = {"file": ("test.txt", io.BytesIO(b"hello"), "text/plain")} r = requests.post( f"{base_url}/api/upload", files=files, timeout=10, ) assert r.status_code == 400 body = r.json() assert body.get("success") is False def test_upload_delete_local(base_url): """DELETE /api/upload 删除本地文件:先上传再删除""" # 1. 上传 files = {"file": ("del_test.jpg", io.BytesIO(_MIN_JPEG), "image/jpeg")} r1 = requests.post( f"{base_url}/api/upload", files=files, data={"folder": "test"}, timeout=10, ) assert r1.status_code == 200 url = r1.json().get("url") assert url, "上传应返回 url" # path 支持 /uploads/xxx 或含 /uploads/ 的完整 URL path = url # 2. 删除 r2 = requests.delete( f"{base_url}/api/upload", params={"path": path}, timeout=10, ) assert r2.status_code == 200 body = r2.json() assert body.get("success") is True assert "删除成功" in body.get("message", "") def test_upload_delete_no_path(base_url): """DELETE /api/upload 无 path 返回 400""" r = requests.delete(f"{base_url}/api/upload", timeout=10) assert r.status_code == 400 body = r.json() assert body.get("success") is False