"""接口测试用的独立环境:必须在导入 app 之前写好环境变量,配置是冻结的 dataclass。""" from __future__ import annotations import os import struct import tempfile import zlib from pathlib import Path import pytest os.environ.setdefault("APP_SECRET", "test-secret-0123456789abcdefghijkl") os.environ.setdefault("ADMIN_USERNAME", "admin") os.environ.setdefault("ADMIN_PASSWORD", "TestAdmin12345") os.environ["DATABASE_URL"] = f"sqlite:///{Path(tempfile.mkdtemp(prefix='laser-test-')) / 'test.db'}" def png_bytes(width: int = 24, height: int = 16) -> bytes: """不依赖 Pillow 手工拼一张真实可解码的 PNG。""" def chunk(tag: bytes, payload: bytes) -> bytes: return struct.pack(">I", len(payload)) + tag + payload + struct.pack(">I", zlib.crc32(tag + payload)) raw = b"".join(b"\x00" + b"\x20\x60\x90" * width for _ in range(height)) header = chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)) return b"\x89PNG\r\n\x1a\n" + header + chunk(b"IDAT", zlib.compress(raw)) + chunk(b"IEND", b"") def jpeg_bytes(width: int = 32, height: int = 20) -> bytes: """只含 SOI/APP0/SOF0/EOI 的最小 JPEG,足以走完服务端的文件头校验。""" app0 = b"\xff\xe0" + struct.pack(">H", 16) + b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00" sof0 = b"\xff\xc0" + struct.pack(">HBHHB", 11, 8, height, width, 1) + b"\x01\x11\x00" return b"\xff\xd8" + app0 + sof0 + b"\xff\xd9" def record_payload(experiment_id: str = "LAS-TEST-001", **overrides) -> dict: payload = { "experiment_id": experiment_id, "test_date": "2026-07-01", "material_category": "木材", "material_name": "椴木板", "material_thickness": 3, "actual_output_power": 8.5, "display_current": 2, "scanning_speed": 100, "pulse_frequency": 20, "pulse_width": 5, "defocus_amount": 0, "scan_line_spacing": 0.1, "filling_method": "双向填充", "processing_size": "20×20", "is_cut_through": True, "carbonized_edge_width": 0.2, "etching_depth": 30, "is_fire_smolder": False, "pattern_clarity_score": 9, "presentation_balance_score": 8, "finished_image_filename": "LAS-TEST-001.jpg", "remarks": "测试记录", } payload.update(overrides) return payload @pytest.fixture(scope="session") def client(): from fastapi.testclient import TestClient from app.main import app with TestClient(app) as test_client: yield test_client @pytest.fixture() def admin(client): """返回带管理员会话的客户端和对应 CSRF 令牌。""" import hashlib import hmac from app.config import settings from app.main import SESSION_COOKIE client.cookies.clear() response = client.post("/api/auth/login", data={"username": settings.admin_username, "password": settings.admin_password}) assert response.status_code == 200, response.text token = client.cookies.get(SESSION_COOKIE) csrf = hmac.new(settings.secret.encode(), token.encode(), hashlib.sha256).hexdigest() return client, {"X-CSRF-Token": csrf}