From 9c4619a80506e35ab8b3c5ddba041f26438468b7 Mon Sep 17 00:00:00 2001 From: Nathan Morin <301673471+nathaq@users.noreply.github.com> Date: Mon, 31 Aug 2026 02:20:36 +0800 Subject: [PATCH] fix: add encoding="utf-8" to review_storage.py open() calls --- backend/app/storage/review_storage.py | 18 ++-- backend/tests/test_pr_02_review_encoding.py | 94 +++++++++++++++++++++ 2 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 backend/tests/test_pr_02_review_encoding.py diff --git a/backend/app/storage/review_storage.py b/backend/app/storage/review_storage.py index 1a3ab094..884c12d3 100644 --- a/backend/app/storage/review_storage.py +++ b/backend/app/storage/review_storage.py @@ -66,7 +66,7 @@ def get_review(review_id: str) -> Optional[Dict[str, Any]]: path = os.path.join(REVIEWS_DIR, review_id, "meta.json") if not os.path.isfile(path): return None - with open(path) as f: + with open(path, encoding="utf-8") as f: return json.load(f) @@ -88,7 +88,7 @@ def list_reviews(paper_id: Optional[str] = None) -> List[Dict[str, Any]]: meta = os.path.join(REVIEWS_DIR, name, "meta.json") if os.path.isfile(meta): try: - with open(meta) as f: + with open(meta, encoding="utf-8") as f: data = json.load(f) if paper_id and data.get("paperId") != paper_id: continue @@ -101,7 +101,7 @@ def list_reviews(paper_id: Optional[str] = None) -> List[Dict[str, Any]]: def _save_record(review_id: str, record: Dict): review_dir = os.path.join(REVIEWS_DIR, review_id) os.makedirs(review_dir, exist_ok=True) - with open(os.path.join(review_dir, "meta.json"), "w") as f: + with open(os.path.join(review_dir, "meta.json"), "w", encoding="utf-8") as f: json.dump(record, f, indent=2, default=str) @@ -140,7 +140,7 @@ def create_improvement_request(data: Dict[str, Any]) -> Dict[str, Any]: } req_dir = os.path.join(IMPROVEMENT_REQUESTS_DIR, req_id) os.makedirs(req_dir, exist_ok=True) - with open(os.path.join(req_dir, "meta.json"), "w") as f: + with open(os.path.join(req_dir, "meta.json"), "w", encoding="utf-8") as f: json.dump(record, f, indent=2, default=str) # Index under review @@ -149,10 +149,10 @@ def create_improvement_request(data: Dict[str, Any]) -> Dict[str, Any]: idx_path = os.path.join(review_dir, "requests.json") idx = [] if os.path.isfile(idx_path): - with open(idx_path) as f: + with open(idx_path, encoding="utf-8") as f: idx = json.load(f) idx.append(record) - with open(idx_path, "w") as f: + with open(idx_path, "w", encoding="utf-8") as f: json.dump(idx, f, indent=2, default=str) return record @@ -166,7 +166,7 @@ def list_improvement_requests(review_id: Optional[str] = None, paper_id: Optiona meta = os.path.join(IMPROVEMENT_REQUESTS_DIR, name, "meta.json") if os.path.isfile(meta): try: - with open(meta) as f: + with open(meta, encoding="utf-8") as f: data = json.load(f) if review_id and data.get("reviewId") != review_id: continue @@ -184,7 +184,7 @@ def get_improvement_request(req_id: str) -> Optional[Dict[str, Any]]: path = os.path.join(IMPROVEMENT_REQUESTS_DIR, req_id, "meta.json") if not os.path.isfile(path): return None - with open(path) as f: + with open(path, encoding="utf-8") as f: return json.load(f) @@ -195,6 +195,6 @@ def update_improvement_request(req_id: str, updates: Dict[str, Any]) -> Optional record.update(updates) record["updatedAt"] = _utcnow_iso() req_dir = os.path.join(IMPROVEMENT_REQUESTS_DIR, req_id) - with open(os.path.join(req_dir, "meta.json"), "w") as f: + with open(os.path.join(req_dir, "meta.json"), "w", encoding="utf-8") as f: json.dump(record, f, indent=2, default=str) return record diff --git a/backend/tests/test_pr_02_review_encoding.py b/backend/tests/test_pr_02_review_encoding.py new file mode 100644 index 00000000..cd27ff7a --- /dev/null +++ b/backend/tests/test_pr_02_review_encoding.py @@ -0,0 +1,94 @@ +"""Test that review_storage.py handles non-ASCII content via encoding=utf-8.""" + +import json +import os +import tempfile +import shutil +from unittest import mock + +import pytest + + +@pytest.fixture +def tmp_reviews_dir(tmp_path): + d = tmp_path / "reviews" + d.mkdir() + return str(d) + + +@pytest.fixture +def tmp_impr_dir(tmp_path): + d = tmp_path / "improvement_requests" + d.mkdir() + return str(d) + + +def test_review_roundtrip_non_ascii(tmp_reviews_dir, tmp_impr_dir): + """Create a review with Chinese characters and verify round-trip integrity.""" + with mock.patch("app.storage.review_storage.REVIEWS_DIR", tmp_reviews_dir), \ + mock.patch("app.storage.review_storage.IMPROVEMENT_REQUESTS_DIR", tmp_impr_dir): + from app.storage import review_storage + + data = { + "paperId": "paper_test", + "reviewerProfile": "senior_reviewer", + "providerName": "moonshot", + "model": "moonshot-v1-8k", + "reviewKind": "standard", + "budgetMode": "balanced", + "ablationMode": "full", + } + record = review_storage.create_review(data) + review_id = record["id"] + + # Update with non-ASCII content + review_storage.update_review(review_id, { + "markdownReport": "# 审阅报告\n\n这是一个测试。", + "findings": [{"text": "发现了严重的逻辑错误"}], + }) + + # Read back + fetched = review_storage.get_review(review_id) + assert fetched is not None + assert "审阅报告" in fetched["markdownReport"] + assert "发现了严重的逻辑错误" in fetched["findings"][0]["text"] + + +def test_list_reviews_non_ascii(tmp_reviews_dir, tmp_impr_dir): + """Verify list_reviews returns non-ASCII content correctly.""" + with mock.patch("app.storage.review_storage.REVIEWS_DIR", tmp_reviews_dir), \ + mock.patch("app.storage.review_storage.IMPROVEMENT_REQUESTS_DIR", tmp_impr_dir): + from app.storage import review_storage + + data = {"paperId": "paper_unicode"} + record = review_storage.create_review(data) + review_storage.update_review(record["id"], { + "markdownReport": "éèê üöä 世界" + }) + + reviews = review_storage.list_reviews(paper_id="paper_unicode") + assert len(reviews) == 1 + assert "éèê" in reviews[0]["markdownReport"] + assert "世界" in reviews[0]["markdownReport"] + + +def test_improvement_request_non_ascii(tmp_reviews_dir, tmp_impr_dir): + """Create an improvement request with non-ASCII fields and verify round-trip.""" + with mock.patch("app.storage.review_storage.REVIEWS_DIR", tmp_reviews_dir), \ + mock.patch("app.storage.review_storage.IMPROVEMENT_REQUESTS_DIR", tmp_impr_dir): + from app.storage import review_storage + + # First create a review so the index dir exists + review_record = review_storage.create_review({"paperId": "p1"}) + + req = review_storage.create_improvement_request({ + "reviewId": review_record["id"], + "paperId": "p1", + "description": "改善建议:添加更多的测试用例", + "suggestedEdit": "Replace with: Überprüfen Sie die Eingabe", + }) + + fetched = review_storage.get_improvement_request(req["id"]) + assert fetched is not None + assert "改善建议" in fetched["description"] + assert "Überprüfen" in fetched["suggestedEdit"]